summaryrefslogtreecommitdiff
path: root/Tools
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-10-15 09:45:50 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-10-15 09:45:50 +0000
commite15dd966d523731101f70ccf768bba12435a0208 (patch)
treeae9cb828a24ded2585a41af3f21411523b47897d /Tools
downloadWebKitGtk-tarball-e15dd966d523731101f70ccf768bba12435a0208.tar.gz
webkitgtk-2.10.2webkitgtk-2.10.2
Diffstat (limited to 'Tools')
-rw-r--r--Tools/CMakeLists.txt38
-rw-r--r--Tools/DumpRenderTree/AccessibilityController.cpp195
-rw-r--r--Tools/DumpRenderTree/AccessibilityController.h107
-rw-r--r--Tools/DumpRenderTree/AccessibilityTextMarker.cpp134
-rw-r--r--Tools/DumpRenderTree/AccessibilityTextMarker.h105
-rw-r--r--Tools/DumpRenderTree/AccessibilityUIElement.cpp1733
-rw-r--r--Tools/DumpRenderTree/AccessibilityUIElement.h314
-rw-r--r--Tools/DumpRenderTree/CMakeLists.txt123
-rw-r--r--Tools/DumpRenderTree/CyclicRedundancyCheck.cpp64
-rw-r--r--Tools/DumpRenderTree/CyclicRedundancyCheck.h38
-rw-r--r--Tools/DumpRenderTree/DefaultPolicyDelegate.h13
-rw-r--r--Tools/DumpRenderTree/DumpRenderTree.h75
-rw-r--r--Tools/DumpRenderTree/DumpRenderTree.sln100
-rw-r--r--Tools/DumpRenderTree/DumpRenderTreeCommon.cpp85
-rw-r--r--Tools/DumpRenderTree/DumpRenderTreeFileDraggingSource.h43
-rw-r--r--Tools/DumpRenderTree/DumpRenderTreePrefix.h38
-rw-r--r--Tools/DumpRenderTree/ForwardingHeaders/runtime/ArrayBufferView.h1
-rw-r--r--Tools/DumpRenderTree/ForwardingHeaders/runtime/JSArrayBufferView.h1
-rw-r--r--Tools/DumpRenderTree/ForwardingHeaders/runtime/JSExportMacros.h1
-rw-r--r--Tools/DumpRenderTree/ForwardingHeaders/runtime/TypedArrayInlines.h1
-rw-r--r--Tools/DumpRenderTree/GCController.cpp100
-rw-r--r--Tools/DumpRenderTree/GCController.h50
-rw-r--r--Tools/DumpRenderTree/JavaScriptThreading.cpp157
-rw-r--r--Tools/DumpRenderTree/JavaScriptThreading.h40
-rw-r--r--Tools/DumpRenderTree/PixelDumpSupport.cpp136
-rw-r--r--Tools/DumpRenderTree/PixelDumpSupport.h45
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/CMakeLists.txt61
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npapi.h1
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npfunctions.h1
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npruntime.h1
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/PluginObject.cpp1309
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/PluginObject.h88
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/PluginTest.cpp311
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/PluginTest.h281
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/TestObject.cpp210
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/TestObject.h30
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/DocumentOpenInDestroyStream.cpp56
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/EvaluateJSAfterRemovingPluginElement.cpp64
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/EvaluateJSWithinNPP_New.cpp56
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/FormValue.cpp53
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLNotifyWithURLThatFailsToLoad.cpp72
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLWithJavaScriptURL.cpp112
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLWithJavaScriptURLDestroyingPlugin.cpp48
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetUserAgentWithNullNPPFromNPPNew.cpp54
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/InvokeDestroysPluginWithinNPP_New.cpp67
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/LogNPPSetWindow.cpp59
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPDeallocateCalledBeforeNPShutdown.cpp94
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPPNewFails.cpp47
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPPSetWindowCalledDuringDestruction.cpp125
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeCallsWithNullNPP.cpp66
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeObjectFromDestroyedPlugin.cpp87
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeRemoveProperty.cpp170
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NullNPPGetValuePointer.cpp70
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PassDifferentNPPStruct.cpp72
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PluginScriptableNPObjectInvokeDefault.cpp68
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PluginScriptableObjectOverridesAllProperties.cpp82
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PrivateBrowsing.cpp106
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/SlowNPPNew.cpp87
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/ToStringAndValueOfObject.cpp81
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/URLRedirect.cpp167
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/Tests/x11/CallInvalidateRectWithNullNPPArgument.cpp50
-rw-r--r--Tools/DumpRenderTree/TestNetscapePlugIn/main.cpp874
-rw-r--r--Tools/DumpRenderTree/TestRunner.cpp2225
-rw-r--r--Tools/DumpRenderTree/TestRunner.h437
-rw-r--r--Tools/DumpRenderTree/WorkQueue.cpp105
-rw-r--r--Tools/DumpRenderTree/WorkQueue.h57
-rw-r--r--Tools/DumpRenderTree/WorkQueueItem.h149
-rw-r--r--Tools/DumpRenderTree/cairo/PixelDumpSupportCairo.cpp93
-rw-r--r--Tools/DumpRenderTree/cairo/PixelDumpSupportCairo.h81
-rw-r--r--Tools/DumpRenderTree/config.h58
-rw-r--r--Tools/ImageDiff/CMakeLists.txt20
-rw-r--r--Tools/ImageDiff/PlatformGTK.cmake11
-rw-r--r--Tools/ImageDiff/gtk/ImageDiff.cpp237
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserCFLite.vsprops11
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserCommon.vsprops16
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserCoreFoundation.vsprops11
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserDebug.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserDebugAll.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserDebugCairoCFLite.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserLauncherCommon.vsprops13
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebug.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebugAll.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebugCairoCFLite.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserLauncherProduction.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserLauncherRelease.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserLauncherReleaseCairoCFLite.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserProduction.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserRelease.vsprops8
-rw-r--r--Tools/MiniBrowser/Configurations/MiniBrowserReleaseCairoCFLite.vsprops8
-rw-r--r--Tools/MiniBrowser/MBToolbarItem.h27
-rw-r--r--Tools/MiniBrowser/MiniBrowser.entitlements10
-rw-r--r--Tools/MiniBrowser/MiniBrowserWebProcessPlugIn.h34
-rw-r--r--Tools/MiniBrowser/gtk/BrowserCellRendererVariant.c346
-rw-r--r--Tools/MiniBrowser/gtk/BrowserCellRendererVariant.h49
-rw-r--r--Tools/MiniBrowser/gtk/BrowserDownloadsBar.c277
-rw-r--r--Tools/MiniBrowser/gtk/BrowserDownloadsBar.h51
-rw-r--r--Tools/MiniBrowser/gtk/BrowserSearchBar.c293
-rw-r--r--Tools/MiniBrowser/gtk/BrowserSearchBar.h57
-rw-r--r--Tools/MiniBrowser/gtk/BrowserSettingsDialog.c190
-rw-r--r--Tools/MiniBrowser/gtk/BrowserSettingsDialog.h50
-rw-r--r--Tools/MiniBrowser/gtk/BrowserWindow.c1229
-rw-r--r--Tools/MiniBrowser/gtk/BrowserWindow.h54
-rw-r--r--Tools/MiniBrowser/gtk/CMakeLists.txt63
-rw-r--r--Tools/MiniBrowser/gtk/browser-marshal.list1
-rw-r--r--Tools/MiniBrowser/gtk/main.c311
-rw-r--r--Tools/Scripts/VCSUtils.pm2215
-rwxr-xr-xTools/Scripts/run-gtk-tests461
-rwxr-xr-xTools/Scripts/webkit-build-directory79
-rwxr-xr-xTools/Scripts/webkitdirs.pm2505
-rw-r--r--Tools/TestWebKitAPI/CMakeLists.txt216
-rw-r--r--Tools/TestWebKitAPI/Configurations/TestWebKitAPICFLite.vsprops11
-rw-r--r--Tools/TestWebKitAPI/Configurations/TestWebKitAPICFNetwork.vsprops11
-rw-r--r--Tools/TestWebKitAPI/Configurations/TestWebKitAPICommon.vsprops16
-rw-r--r--Tools/TestWebKitAPI/Configurations/TestWebKitAPICoreFoundation.vsprops11
-rw-r--r--Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebug.vsprops8
-rw-r--r--Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebugAll.vsprops8
-rw-r--r--Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebugCairoCFLite.vsprops8
-rw-r--r--Tools/TestWebKitAPI/Configurations/TestWebKitAPIInjectedBundleCommon.vsprops11
-rw-r--r--Tools/TestWebKitAPI/Configurations/TestWebKitAPIRelease.vsprops8
-rw-r--r--Tools/TestWebKitAPI/Configurations/TestWebKitAPIReleaseCairoCFLite.vsprops8
-rw-r--r--Tools/TestWebKitAPI/Counters.cpp34
-rw-r--r--Tools/TestWebKitAPI/Counters.h88
-rw-r--r--Tools/TestWebKitAPI/ForwardingHeaders/WebCore/LayoutUnit.h4
-rw-r--r--Tools/TestWebKitAPI/InjectedBundleController.cpp145
-rw-r--r--Tools/TestWebKitAPI/InjectedBundleController.h70
-rw-r--r--Tools/TestWebKitAPI/InjectedBundleMain.cpp43
-rw-r--r--Tools/TestWebKitAPI/InjectedBundleTest.h72
-rw-r--r--Tools/TestWebKitAPI/JavaScriptTest.cpp97
-rw-r--r--Tools/TestWebKitAPI/JavaScriptTest.h47
-rw-r--r--Tools/TestWebKitAPI/PlatformGTK.cmake164
-rw-r--r--Tools/TestWebKitAPI/PlatformUtilities.cpp95
-rw-r--r--Tools/TestWebKitAPI/PlatformUtilities.h81
-rw-r--r--Tools/TestWebKitAPI/PlatformWebView.h91
-rw-r--r--Tools/TestWebKitAPI/Test.h45
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp56
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/BloomFilter.cpp250
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/CString.cpp196
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/CheckedArithmeticOperations.cpp487
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/Condition.cpp257
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/DateMath.cpp208
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/Deque.cpp191
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/Functional.cpp218
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp511
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp280
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/IntegerToStringConversion.cpp132
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp269
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/Lock.cpp160
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/MD5.cpp47
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/MathExtras.cpp177
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp214
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp957
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/MoveOnly.h98
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/NakedPtr.cpp230
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/Optional.cpp96
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/ParkingLot.cpp273
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/RedBlackTree.cpp322
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/Ref.cpp161
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/RefCounter.cpp158
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/RefLogger.h56
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp398
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/RunLoop.cpp57
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/SHA1.cpp57
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/SaturatedArithmeticOperations.cpp105
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/StringBuilder.cpp338
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp444
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp548
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/StringOperators.cpp187
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/StringView.cpp736
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/TemporaryChange.cpp47
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/Vector.cpp617
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp284
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp187
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/WorkQueue.cpp203
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/darwin/OSObjectPtr.cpp66
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/glib/GMainLoopSource.cpp547
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/glib/GUniquePtr.cpp195
-rw-r--r--Tools/TestWebKitAPI/Tests/WTF/glib/WorkQueueGLib.cpp71
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/CSSParser.cpp88
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/CalculationValue.cpp283
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp2793
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp121
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h67
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp121
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/FileSystem.cpp96
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/LayoutUnit.cpp249
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/PublicSuffix.cpp78
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/SharedBuffer.cpp119
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/TimeRanges.cpp293
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/URL.cpp82
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/gtk/UserAgentQuirks.cpp48
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/18-characters.html0
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/AboutBlankLoad.cpp64
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CanHandleRequest.cpp78
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CanHandleRequest_Bundle.cpp72
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CloseFromWithinCreatePage.cpp87
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CloseThenTerminate.cpp69
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CookieManager.cpp93
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewIsActiveSetIsActive.cpp131
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewIsActiveSetIsActive_Bundle.cpp52
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewRestoreZoomAndScrollBackForward.cpp129
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewUserViewportToContents.cpp145
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/backforward1.html5
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/backforward2.html1
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic.cpp140
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic_Bundle.cpp264
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionNoCache.cpp139
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionNoCache_Bundle.cpp283
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/DidAssociateFormControls.cpp90
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/DidAssociateFormControls_Bundle.cpp83
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/DidNotHandleKeyDown.cpp83
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/DocumentStartUserScriptAlertCrash.cpp72
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/DocumentStartUserScriptAlertCrash_Bundle.cpp60
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/DownloadDecideDestinationCrash.cpp90
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/EphemeralSessionPushStateNoHistoryCallback.cpp83
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/EvaluateJavaScript.cpp65
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/FailedLoad.cpp72
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/Find.cpp86
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ForceRepaint.cpp74
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/FrameMIMETypeHTML.cpp82
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/FrameMIMETypePNG.cpp81
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/Geolocation.cpp342
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/GetInjectedBundleInitializationUserDataCallback.cpp72
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/GetInjectedBundleInitializationUserDataCallback_Bundle.cpp53
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/HitTestResultNodeHandle.cpp93
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/HitTestResultNodeHandle_Bundle.cpp71
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleBasic.cpp84
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleBasic_Bundle.cpp54
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleFrameHitTest.cpp73
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleFrameHitTest_Bundle.cpp83
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleInitializationUserDataCallbackWins.cpp74
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleInitializationUserDataCallbackWins_Bundle.cpp53
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/LayoutMilestonesWithAllContentInFrame.cpp71
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/LoadAlternateHTMLStringWithNonDirectoryURL.cpp85
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/LoadCanceledNoServerRedirectCallback.cpp100
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/LoadCanceledNoServerRedirectCallback_Bundle.cpp77
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/LoadPageOnCrash.cpp110
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/MenuTypesForMouseEvents.cpp149
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ModalAlertsSPI.cpp123
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/MouseMoveAfterCrash.cpp93
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/MouseMoveAfterCrash_Bundle.cpp61
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayout.cpp93
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFails.cpp93
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFails_Bundle.cpp56
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutForImages.cpp93
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutForImages_Bundle.cpp56
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFrames.cpp97
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFrames_Bundle.cpp56
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayout_Bundle.cpp56
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/PageLoadBasic.cpp180
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/PageLoadDidChangeLocationWithinPageForFrame.cpp91
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ParentFrame.cpp75
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ParentFrame_Bundle.cpp84
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/PasteboardNotifications_Bundle.cpp89
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/PreventEmptyUserAgent.cpp75
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/PrivateBrowsingPushStateNoHistoryCallback.cpp101
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ReloadPageAfterCrash.cpp93
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ResizeReversePaginatedWebView.cpp95
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ResizeWindowAfterCrash.cpp104
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ResponsivenessTimerDoesntFireEarly.cpp103
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ResponsivenessTimerDoesntFireEarly_Bundle.cpp64
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/RestoreSessionStateContainingFormData.cpp96
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ScrollPinningBehaviors.cpp102
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/SeccompFilters.cpp441
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ShouldGoToBackForwardListItem.cpp97
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ShouldGoToBackForwardListItem_Bundle.cpp75
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/ShouldKeepCurrentBackForwardListItemInList.cpp163
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/SpacebarScrolling.cpp114
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/StopLoadingDuringDidFailProvisionalLoad.cpp83
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/StopLoadingDuringDidFailProvisionalLoad_bundle.cpp81
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/TerminateTwice.cpp75
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/TextFieldDidBeginAndEndEditing.cpp131
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/TextFieldDidBeginAndEndEditing_Bundle.cpp76
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/UserMedia.cpp66
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/UserMessage.cpp162
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/UserMessage_Bundle.cpp57
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKBundleFileHandle.cpp86
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKBundleFileHandle_Bundle.cpp99
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKImageCreateCGImageCrash.cpp41
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKPageConfiguration.cpp82
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKPageCopySessionStateWithFiltering.cpp136
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKPageGetScaleFactorNotZero.cpp84
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKPageIsPlayingAudio.cpp152
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKPreferences.cpp125
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKString.cpp75
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKStringJSString.cpp55
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WKURL.cpp43
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WebArchive.cpp135
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WebArchive_Bundle.cpp69
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WebCoreStatisticsWithNoWebProcess.cpp55
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WillLoad.cpp244
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WillLoad_Bundle.cpp89
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WillSendSubmitEvent.cpp84
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/WillSendSubmitEvent_Bundle.cpp70
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/all-content-in-one-iframe.html1
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/associate-form-controls.html31
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/auto-submitting-form.html20
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/bundle-file.html16
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/close-from-within-create-page.html16
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/custom-protocol-sync-xhr.html6
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/execCopy.html15
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/file-with-anchor.html19
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/file-with-mse.html46
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/file-with-video.html18
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/find.html5
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/findRanges.html11
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/geolocationGetCurrentPosition.html3
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/geolocationGetCurrentPositionWithHighAccuracy.html3
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/geolocationWatchPosition.html3
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/geolocationWatchPositionWithHighAccuracy.html3
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/getUserMedia.html14
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/gtk/InputMethodFilter.cpp258
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/icon.pngbin0 -> 36541 bytes
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/input-focus-blur.html18
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/link-with-title.html5
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/lots-of-iframes.html35
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/lots-of-images.html17
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/lots-of-text-vertical-lr.html3
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/lots-of-text.html3
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/modal-alerts-in-new-about-blank-window.html13
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/mouse-button-listener.html30
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/mouse-move-listener.html16
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/open-and-close-window.html11
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/push-state.html3
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/should-open-external-schemes.html21
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/simple-accelerated-compositing.html5
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/simple-form.html11
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/simple-iframe.html6
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/simple-tall.html7
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/simple.html5
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/simple2.html5
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/simple3.html5
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/spacebar-scrolling.html26
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/test-mse.mp4bin0 -> 80933 bytes
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2/test.mp4bin0 -> 192844 bytes
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/AccessibilityTestServer.cpp58
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt128
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMDOMWindowTest.cpp221
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp204
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp273
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp124
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/EditorTest.cpp131
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/FrameTest.cpp80
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/InspectorTestServer.cpp58
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAuthentication.cpp304
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestBackForwardList.cpp280
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestContextMenu.cpp1056
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestCookieManager.cpp326
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMDOMWindow.cpp152
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp87
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNodeFilter.cpp55
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMXPathNSResolver.cpp52
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp633
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestEditor.cpp42
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestFrame.cpp59
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspector.cpp369
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspectorServer.cpp294
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestLoaderClient.cpp527
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestMultiprocess.cpp284
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestPrinting.cpp304
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestResources.cpp858
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestSSL.cpp434
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp984
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebExtensions.cpp232
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitAccessibility.cpp224
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFaviconDatabase.cpp250
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFindController.cpp335
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitPolicyClient.cpp268
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSettings.cpp379
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitUserContentManager.cpp388
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitVersion.cpp50
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp570
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp865
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebViewEditor.cpp470
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp373
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.cpp121
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.h39
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/blank.icobin0 -> 198 bytes
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/link-title.js1
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-cert.pem13
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-key.pem16
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/webkit2gtk-tests.gresource.xml6
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2ObjC/CustomProtocolsInvalidScheme_Bundle.cpp66
-rw-r--r--Tools/TestWebKitAPI/Tests/WebKit2ObjC/PreventImageLoadWithAutoResizing_Bundle.cpp69
-rw-r--r--Tools/TestWebKitAPI/TestsController.cpp83
-rw-r--r--Tools/TestWebKitAPI/TestsController.h48
-rw-r--r--Tools/TestWebKitAPI/WTFStringUtilities.h48
-rw-r--r--Tools/TestWebKitAPI/config.h91
-rw-r--r--Tools/TestWebKitAPI/gtk/InjectedBundleControllerGtk.cpp35
-rw-r--r--Tools/TestWebKitAPI/gtk/PlatformUtilitiesGtk.cpp96
-rw-r--r--Tools/TestWebKitAPI/gtk/PlatformWebViewGtk.cpp148
-rw-r--r--Tools/TestWebKitAPI/gtk/WebKit2Gtk/LoadTrackingTest.cpp243
-rw-r--r--Tools/TestWebKitAPI/gtk/WebKit2Gtk/LoadTrackingTest.h71
-rw-r--r--Tools/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.cpp87
-rw-r--r--Tools/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.h167
-rw-r--r--Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestBus.cpp106
-rw-r--r--Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestBus.h44
-rw-r--r--Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestServer.cpp64
-rw-r--r--Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestServer.h49
-rw-r--r--Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.cpp518
-rw-r--r--Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.h99
-rw-r--r--Tools/TestWebKitAPI/gtk/main.cpp36
-rw-r--r--Tools/WebKitTestRunner/CMakeLists.txt108
-rw-r--r--Tools/WebKitTestRunner/CyclicRedundancyCheck.cpp64
-rw-r--r--Tools/WebKitTestRunner/CyclicRedundancyCheck.h38
-rw-r--r--Tools/WebKitTestRunner/DerivedSources.make55
-rw-r--r--Tools/WebKitTestRunner/EventSenderProxy.h137
-rw-r--r--Tools/WebKitTestRunner/GeolocationProviderMock.cpp116
-rw-r--r--Tools/WebKitTestRunner/GeolocationProviderMock.h61
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.cpp115
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h84
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarker.cpp76
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarker.h75
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarkerRange.cpp76
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarkerRange.h73
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.cpp241
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h313
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/ActivateFonts.h35
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityController.idl45
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityTextMarker.idl29
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityTextMarkerRange.idl29
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl227
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm570
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/EventSendingController.idl69
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/GCController.idl31
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrappable.h47
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrapper.cpp80
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrapper.h57
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl206
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/TextInputController.idl32
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/EventSendingController.cpp793
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/EventSendingController.h93
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/GCController.cpp75
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/GCController.h54
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp633
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h177
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/InjectedBundleMain.cpp35
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp1993
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.h183
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp859
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/TestRunner.h345
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/TextInputController.cpp80
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/TextInputController.h55
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityControllerAtk.cpp148
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp259
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.h60
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp2026
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/gtk/ActivateFontsGtk.cpp160
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleGtk.cpp52
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleUtilities.cpp52
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleUtilities.h40
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/gtk/TestRunnerGtk.cpp70
-rw-r--r--Tools/WebKitTestRunner/Options.cpp192
-rw-r--r--Tools/WebKitTestRunner/Options.h79
-rw-r--r--Tools/WebKitTestRunner/PixelDumpSupport.cpp94
-rw-r--r--Tools/WebKitTestRunner/PixelDumpSupport.h34
-rw-r--r--Tools/WebKitTestRunner/PlatformGTK.cmake64
-rw-r--r--Tools/WebKitTestRunner/PlatformWebView.h104
-rw-r--r--Tools/WebKitTestRunner/StringFunctions.h101
-rw-r--r--Tools/WebKitTestRunner/TestController.cpp1653
-rw-r--r--Tools/WebKitTestRunner/TestController.h285
-rw-r--r--Tools/WebKitTestRunner/TestInvocation.cpp683
-rw-r--r--Tools/WebKitTestRunner/TestInvocation.h94
-rw-r--r--Tools/WebKitTestRunner/ViewOptions.h42
-rw-r--r--Tools/WebKitTestRunner/WebKitTestRunner.sln104
-rw-r--r--Tools/WebKitTestRunner/WebKitTestRunnerApp/AppDelegate.h15
-rw-r--r--Tools/WebKitTestRunner/WebKitTestRunnerPrefix.h46
-rw-r--r--Tools/WebKitTestRunner/WebNotificationProvider.cpp160
-rw-r--r--Tools/WebKitTestRunner/WebNotificationProvider.h58
-rw-r--r--Tools/WebKitTestRunner/WorkQueueManager.cpp230
-rw-r--r--Tools/WebKitTestRunner/WorkQueueManager.h63
-rw-r--r--Tools/WebKitTestRunner/cairo/TestInvocationCairo.cpp124
-rw-r--r--Tools/WebKitTestRunner/config.h41
-rw-r--r--Tools/WebKitTestRunner/gtk/EventSenderProxyGtk.cpp580
-rw-r--r--Tools/WebKitTestRunner/gtk/PlatformWebViewGtk.cpp161
-rw-r--r--Tools/WebKitTestRunner/gtk/TestControllerGtk.cpp129
-rw-r--r--Tools/WebKitTestRunner/gtk/fonts/AHEM____.TTFbin0 -> 12480 bytes
-rw-r--r--Tools/WebKitTestRunner/gtk/fonts/FontWithNoValidEncoding.fonbin0 -> 8368 bytes
-rw-r--r--Tools/WebKitTestRunner/gtk/fonts/fonts.conf435
-rw-r--r--Tools/WebKitTestRunner/gtk/main.cpp46
-rwxr-xr-xTools/gtk/check-for-webkitdom-api-breaks63
-rw-r--r--Tools/gtk/common.py150
-rwxr-xr-xTools/gtk/generate-gtkdoc211
-rwxr-xr-xTools/gtk/generate-inspector-gresource-manifest.py77
-rw-r--r--Tools/gtk/gtkdoc.py439
-rwxr-xr-xTools/gtk/install-dependencies401
-rw-r--r--Tools/gtk/jhbuild-optional.modules59
-rw-r--r--Tools/gtk/jhbuild.modules414
-rw-r--r--Tools/gtk/jhbuildrc39
-rwxr-xr-xTools/gtk/make-dist.py327
-rw-r--r--Tools/gtk/manifest.txt.in105
-rw-r--r--Tools/gtk/patches/fontconfig-C-11-requires-a-space-between-literal-and-identifier.patch30
-rw-r--r--Tools/gtk/patches/freetype6-2.4.11-truetype-font-height-fix.patch39
-rw-r--r--Tools/gtk/patches/glib-warning-fix.patch34
-rw-r--r--Tools/gtk/patches/gst-plugins-bad-remove-gnustep-support.patch325
-rw-r--r--Tools/gtk/patches/gst-plugins-base-rtp-rtcpbuffer-fix-typo-in-enum.patch45
-rw-r--r--Tools/gtk/patches/librsvg-2.36.1-bump-up-config.guess-to-support-aarch64.patch1581
-rw-r--r--Tools/gtk/patches/llvm-elf-add-stackmaps-arm64.patch13
-rw-r--r--Tools/gtk/patches/llvm-elf-add-stackmaps.patch46
-rw-r--r--Tools/gtk/patches/llvm-elf-allow-fde-references-outside-the-2gb-range-arm64.patch23
-rw-r--r--Tools/gtk/patches/llvm-elf-allow-fde-references-outside-the-2gb-range.patch281
-rw-r--r--Tools/gtk/patches/mesa-gallivm-Fix-build-after-LLVM-commit-211259.patch29
-rw-r--r--Tools/gtk/patches/rtspsrc-timeout-on-udpsrc-is-in-nanoseconds.patch27
-rw-r--r--Tools/gtk/patches/udpsrc-improve-timeouts.patch53
-rw-r--r--Tools/gtk/patches/xserver-fix-glx-init.patch14
-rw-r--r--Tools/gtk/patches/xserver-remove-bogus-dependencies.patch43
-rwxr-xr-xTools/gtk/webkitdom.py261
-rw-r--r--Tools/gtk/ycm_extra_conf.py133
-rw-r--r--Tools/jhbuild/jhbuildutils.py54
508 files changed, 85893 insertions, 0 deletions
diff --git a/Tools/CMakeLists.txt b/Tools/CMakeLists.txt
new file mode 100644
index 000000000..40bdc9de9
--- /dev/null
+++ b/Tools/CMakeLists.txt
@@ -0,0 +1,38 @@
+if ("${PORT}" STREQUAL "Efl")
+ if (DEVELOPER_MODE)
+ add_subdirectory(WebKitTestRunner)
+ add_subdirectory(ImageDiff)
+ if (ENABLE_X11_TARGET)
+ add_subdirectory(DumpRenderTree/TestNetscapePlugIn)
+ endif ()
+ endif ()
+
+ if (ENABLE_MINIBROWSER)
+ add_subdirectory(MiniBrowser/efl)
+ endif ()
+elseif ("${PORT}" STREQUAL "GTK")
+ if (DEVELOPER_MODE)
+ add_subdirectory(WebKitTestRunner)
+ add_subdirectory(ImageDiff)
+ if (ENABLE_API_TESTS)
+ add_subdirectory(TestWebKitAPI/Tests/WebKit2Gtk)
+ endif ()
+ if (ENABLE_X11_TARGET)
+ add_subdirectory(DumpRenderTree/TestNetscapePlugIn)
+ endif ()
+ endif ()
+
+ if (ENABLE_MINIBROWSER)
+ add_subdirectory(MiniBrowser/gtk)
+ endif ()
+endif ()
+
+if (WIN32)
+ add_subdirectory(DumpRenderTree)
+ add_subdirectory(TestWebKitAPI)
+ add_subdirectory(WinLauncher)
+endif ()
+
+if (ENABLE_WEBKIT2 AND ENABLE_API_TESTS)
+ add_subdirectory(TestWebKitAPI)
+endif ()
diff --git a/Tools/DumpRenderTree/AccessibilityController.cpp b/Tools/DumpRenderTree/AccessibilityController.cpp
new file mode 100644
index 000000000..d035dea99
--- /dev/null
+++ b/Tools/DumpRenderTree/AccessibilityController.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if HAVE(ACCESSIBILITY)
+
+#include "AccessibilityController.h"
+
+#include "AccessibilityUIElement.h"
+#include <JavaScriptCore/JSRetainPtr.h>
+
+// Static Value Getters
+
+static JSValueRef getFocusedElementCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, controller->focusedElement());
+}
+
+static JSValueRef getRootElementCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, controller->rootElement());
+}
+
+// Object Creation
+
+void AccessibilityController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> accessibilityControllerStr(Adopt, JSStringCreateWithUTF8CString("accessibilityController"));
+
+ JSClassRef classRef = getJSClass();
+ JSValueRef accessibilityControllerObject = JSObjectMake(context, classRef, this);
+ JSClassRelease(classRef);
+
+ JSObjectSetProperty(context, windowObject, accessibilityControllerStr.get(), accessibilityControllerObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
+}
+
+static JSValueRef logFocusEventsCallback(JSContextRef ctx, JSObjectRef, JSObjectRef thisObject, size_t, const JSValueRef[], JSValueRef*)
+{
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ controller->setLogFocusEvents(true);
+ return JSValueMakeUndefined(ctx);
+}
+
+static JSValueRef logValueChangeEventsCallback(JSContextRef ctx, JSObjectRef, JSObjectRef thisObject, size_t, const JSValueRef[], JSValueRef*)
+{
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ controller->setLogValueChangeEvents(true);
+ return JSValueMakeUndefined(ctx);
+}
+
+static JSValueRef logScrollingStartEventsCallback(JSContextRef ctx, JSObjectRef, JSObjectRef thisObject, size_t, const JSValueRef[], JSValueRef*)
+{
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ controller->setLogScrollingStartEvents(true);
+ return JSValueMakeUndefined(ctx);
+}
+
+static JSValueRef logAccessibilityEventsCallback(JSContextRef ctx, JSObjectRef, JSObjectRef thisObject, size_t, const JSValueRef[], JSValueRef*)
+{
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ controller->setLogAccessibilityEvents(true);
+ return JSValueMakeUndefined(ctx);
+}
+
+static JSValueRef getElementAtPointCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int x = 0;
+ int y = 0;
+ if (argumentCount == 2) {
+ x = JSValueToNumber(context, arguments[0], exception);
+ y = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, controller->elementAtPoint(x, y));
+}
+
+static JSValueRef getAccessibleElementByIdCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSStringRef idAttribute = 0;
+ if (argumentCount == 1)
+ idAttribute = JSValueToStringCopy(context, arguments[0], exception);
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ JSValueRef result = AccessibilityUIElement::makeJSAccessibilityUIElement(context, controller->accessibleElementById(idAttribute));
+ if (idAttribute)
+ JSStringRelease(idAttribute);
+ return result;
+}
+
+static JSValueRef addNotificationListenerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 1)
+ return JSValueMakeBoolean(context, false);
+
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ JSObjectRef callback = JSValueToObject(context, arguments[0], exception);
+ bool succeeded = controller->addNotificationListener(callback);
+ return JSValueMakeBoolean(context, succeeded);
+}
+
+static JSValueRef removeNotificationListenerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ controller->removeNotificationListener();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef enableEnhancedAccessibilityCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ if (argumentCount == 1)
+ controller->enableEnhancedAccessibility(JSValueToBoolean(context, arguments[0]));
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef getEnhancedAccessibilityEnabledCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ return JSValueMakeBoolean(context, controller->enhancedAccessibilityEnabled());
+}
+
+static JSValueRef getPlatformNameCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ JSRetainPtr<JSStringRef> platformName(controller->platformName());
+ if (!platformName.get())
+ return JSValueMakeUndefined(context);
+ return JSValueMakeString(context, platformName.get());
+}
+
+JSClassRef AccessibilityController::getJSClass()
+{
+ static JSStaticFunction staticFunctions[] = {
+ { "logFocusEvents", logFocusEventsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "logValueChangeEvents", logValueChangeEventsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "logScrollingStartEvents", logScrollingStartEventsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "logAccessibilityEvents", logAccessibilityEventsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "elementAtPoint", getElementAtPointCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "accessibleElementById", getAccessibleElementByIdCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "addNotificationListener", addNotificationListenerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "removeNotificationListener", removeNotificationListenerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "enableEnhancedAccessibility", enableEnhancedAccessibilityCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { 0, 0, 0 }
+ };
+
+ static JSStaticValue staticValues[] = {
+ { "focusedElement", getFocusedElementCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "rootElement", getRootElementCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "enhancedAccessibilityEnabled", getEnhancedAccessibilityEnabledCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "platformName", getPlatformNameCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { 0, 0, 0, 0 }
+ };
+
+ static JSClassDefinition classDefinition = {
+ 0, kJSClassAttributeNone, "AccessibilityController", 0, staticValues, staticFunctions,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ return JSClassCreate(&classDefinition);
+}
+
+void AccessibilityController::resetToConsistentState()
+{
+ setLogFocusEvents(false);
+ setLogValueChangeEvents(false);
+ setLogScrollingStartEvents(false);
+ setLogAccessibilityEvents(false);
+ platformResetToConsistentState();
+}
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Tools/DumpRenderTree/AccessibilityController.h b/Tools/DumpRenderTree/AccessibilityController.h
new file mode 100644
index 000000000..9f66dd6ee
--- /dev/null
+++ b/Tools/DumpRenderTree/AccessibilityController.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityController_h
+#define AccessibilityController_h
+
+#include "AccessibilityUIElement.h"
+#include <JavaScriptCore/JSObjectRef.h>
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <string>
+#include <wtf/HashMap.h>
+#include <wtf/Platform.h>
+#if PLATFORM(WIN)
+#include <windows.h>
+#endif
+#if HAVE(ACCESSIBILITY) && (PLATFORM(GTK) || PLATFORM(EFL))
+#include "AccessibilityNotificationHandlerAtk.h"
+#include <atk/atk.h>
+#endif
+
+class AccessibilityController {
+public:
+ AccessibilityController();
+ ~AccessibilityController();
+
+ void makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception);
+
+ // Controller Methods - platform-independent implementations
+ AccessibilityUIElement rootElement();
+ AccessibilityUIElement focusedElement();
+ AccessibilityUIElement elementAtPoint(int x, int y);
+ AccessibilityUIElement accessibleElementById(JSStringRef id);
+
+ void setLogFocusEvents(bool);
+ void setLogValueChangeEvents(bool);
+ void setLogScrollingStartEvents(bool);
+ void setLogAccessibilityEvents(bool);
+
+ void resetToConsistentState();
+
+ // Global notification listener, captures notifications on any object.
+ bool addNotificationListener(JSObjectRef functionCallback);
+ void removeNotificationListener();
+
+ // Enhanced accessibility.
+ void enableEnhancedAccessibility(bool);
+ bool enhancedAccessibilityEnabled();
+
+ JSRetainPtr<JSStringRef> platformName() const;
+
+#if PLATFORM(WIN)
+ // Helper methods so this class can add the listeners on behalf of AccessibilityUIElement.
+ void winAddNotificationListener(PlatformUIElement, JSObjectRef functionCallback);
+ void winNotificationReceived(PlatformUIElement, const std::string& eventName);
+#endif
+
+#if HAVE(ACCESSIBILITY) && (PLATFORM(GTK) || PLATFORM(EFL))
+ AtkObject* childElementById(AtkObject* parent, const char* id);
+#endif
+
+private:
+ static JSClassRef getJSClass();
+
+#if PLATFORM(WIN)
+ HWINEVENTHOOK m_focusEventHook;
+ HWINEVENTHOOK m_valueChangeEventHook;
+ HWINEVENTHOOK m_scrollingStartEventHook;
+
+ HWINEVENTHOOK m_allEventsHook;
+ HWINEVENTHOOK m_notificationsEventHook;
+ HashMap<PlatformUIElement, JSObjectRef> m_notificationListeners;
+#endif
+
+#if PLATFORM(COCOA) || PLATFORM(IOS)
+ RetainPtr<NotificationHandler> m_globalNotificationHandler;
+#endif
+
+#if HAVE(ACCESSIBILITY) && (PLATFORM(GTK) || PLATFORM(EFL))
+ RefPtr<AccessibilityNotificationHandler> m_globalNotificationHandler;
+#endif
+
+ void platformResetToConsistentState();
+};
+
+#endif // AccessibilityController_h
diff --git a/Tools/DumpRenderTree/AccessibilityTextMarker.cpp b/Tools/DumpRenderTree/AccessibilityTextMarker.cpp
new file mode 100644
index 000000000..12504978b
--- /dev/null
+++ b/Tools/DumpRenderTree/AccessibilityTextMarker.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AccessibilityTextMarker.h"
+
+#include "AccessibilityUIElement.h"
+#include <JavaScriptCore/JSRetainPtr.h>
+
+// MARK: AccessibilityTextMarker
+
+// Callback methods
+
+AccessibilityTextMarker* toTextMarker(JSObjectRef object)
+{
+ return static_cast<AccessibilityTextMarker*>(JSObjectGetPrivate(object));
+}
+
+static JSValueRef isMarkerEqualCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 1)
+ return JSValueMakeBoolean(context, false);
+
+ JSObjectRef otherMarker = JSValueToObject(context, arguments[0], exception);
+ return JSValueMakeBoolean(context, toTextMarker(thisObject)->isEqual(toTextMarker(otherMarker)));
+}
+
+// Destruction
+
+static void markerFinalize(JSObjectRef thisObject)
+{
+ delete toTextMarker(thisObject);
+}
+
+// Object Creation
+
+JSObjectRef AccessibilityTextMarker::makeJSAccessibilityTextMarker(JSContextRef context, const AccessibilityTextMarker& element)
+{
+ return JSObjectMake(context, AccessibilityTextMarker::getJSClass(), new AccessibilityTextMarker(element));
+}
+
+JSClassRef AccessibilityTextMarker::getJSClass()
+{
+ static JSStaticValue staticValues[] = {
+ { 0, 0, 0, 0 }
+ };
+
+ static JSStaticFunction staticFunctions[] = {
+ { "isEqual", isMarkerEqualCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { 0, 0, 0 }
+ };
+
+ static JSClassDefinition classDefinition = {
+ 0, kJSClassAttributeNone, "AccessibilityTextMarker", 0, staticValues, staticFunctions,
+ 0, markerFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ static JSClassRef accessibilityTextMarkerClass = JSClassCreate(&classDefinition);
+ return accessibilityTextMarkerClass;
+}
+
+// MARK: AccessibilityTextMarkerRange
+
+// Callback methods
+
+AccessibilityTextMarkerRange* toTextMarkerRange(JSObjectRef object)
+{
+ return static_cast<AccessibilityTextMarkerRange*>(JSObjectGetPrivate(object));
+}
+
+static JSValueRef isMarkerRangeEqualCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 1)
+ return JSValueMakeBoolean(context, false);
+
+ JSObjectRef otherMarker = JSValueToObject(context, arguments[0], exception);
+ return JSValueMakeBoolean(context, toTextMarkerRange(thisObject)->isEqual(toTextMarkerRange(otherMarker)));
+}
+
+// Destruction
+
+static void markerRangeFinalize(JSObjectRef thisObject)
+{
+ delete toTextMarkerRange(thisObject);
+}
+
+// Object Creation
+
+JSObjectRef AccessibilityTextMarkerRange::makeJSAccessibilityTextMarkerRange(JSContextRef context, const AccessibilityTextMarkerRange& element)
+{
+ return JSObjectMake(context, AccessibilityTextMarkerRange::getJSClass(), new AccessibilityTextMarkerRange(element));
+}
+
+JSClassRef AccessibilityTextMarkerRange::getJSClass()
+{
+ static JSStaticValue staticValues[] = {
+ { 0, 0, 0, 0 }
+ };
+
+ static JSStaticFunction staticFunctions[] = {
+ { "isEqual", isMarkerRangeEqualCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { 0, 0, 0 }
+ };
+
+ static JSClassDefinition classDefinition = {
+ 0, kJSClassAttributeNone, "AccessibilityTextMarkerRange", 0, staticValues, staticFunctions,
+ 0, markerRangeFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ static JSClassRef accessibilityTextMarkerRangeClass = JSClassCreate(&classDefinition);
+ return accessibilityTextMarkerRangeClass;
+}
diff --git a/Tools/DumpRenderTree/AccessibilityTextMarker.h b/Tools/DumpRenderTree/AccessibilityTextMarker.h
new file mode 100644
index 000000000..7e17b1492
--- /dev/null
+++ b/Tools/DumpRenderTree/AccessibilityTextMarker.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityTextMarker_h
+#define AccessibilityTextMarker_h
+
+#include <JavaScriptCore/JSObjectRef.h>
+
+#if PLATFORM(MAC) && !PLATFORM(IOS)
+#define SUPPORTS_AX_TEXTMARKERS 1
+#else
+#define SUPPORTS_AX_TEXTMARKERS 0
+#endif
+
+#if PLATFORM(COCOA)
+#include <wtf/RetainPtr.h>
+typedef CFTypeRef PlatformTextMarker;
+typedef CFTypeRef PlatformTextMarkerRange;
+#else
+typedef void* PlatformTextMarker;
+typedef void* PlatformTextMarkerRange;
+#endif
+
+class AccessibilityUIElement;
+
+class AccessibilityTextMarker {
+public:
+ AccessibilityTextMarker(PlatformTextMarker);
+ AccessibilityTextMarker(const AccessibilityTextMarker&);
+ ~AccessibilityTextMarker();
+
+ PlatformTextMarker platformTextMarker() const;
+
+ static JSObjectRef makeJSAccessibilityTextMarker(JSContextRef, const AccessibilityTextMarker&);
+ bool isEqual(AccessibilityTextMarker*);
+
+private:
+ static JSClassRef getJSClass();
+#if SUPPORTS_AX_TEXTMARKERS
+ RetainPtr<PlatformTextMarker> m_textMarker;
+#else
+ PlatformTextMarker m_textMarker;
+#endif
+};
+
+class AccessibilityTextMarkerRange {
+public:
+ AccessibilityTextMarkerRange(PlatformTextMarkerRange);
+ AccessibilityTextMarkerRange(const AccessibilityTextMarkerRange&);
+ ~AccessibilityTextMarkerRange();
+
+ PlatformTextMarkerRange platformTextMarkerRange() const;
+
+ static JSObjectRef makeJSAccessibilityTextMarkerRange(JSContextRef, const AccessibilityTextMarkerRange&);
+ bool isEqual(AccessibilityTextMarkerRange*);
+
+private:
+ static JSClassRef getJSClass();
+#if SUPPORTS_AX_TEXTMARKERS
+ RetainPtr<PlatformTextMarkerRange> m_textMarkerRange;
+#else
+ PlatformTextMarkerRange m_textMarkerRange;
+#endif
+};
+
+AccessibilityTextMarker* toTextMarker(JSObjectRef object);
+AccessibilityTextMarkerRange* toTextMarkerRange(JSObjectRef object);
+
+#if !SUPPORTS_AX_TEXTMARKERS
+inline AccessibilityTextMarker::AccessibilityTextMarker(PlatformTextMarker) { }
+inline AccessibilityTextMarker::AccessibilityTextMarker(const AccessibilityTextMarker&) { }
+inline AccessibilityTextMarker::~AccessibilityTextMarker() { }
+inline bool AccessibilityTextMarker::isEqual(AccessibilityTextMarker*) { return false; }
+inline PlatformTextMarker AccessibilityTextMarker::platformTextMarker() const { return m_textMarker; }
+
+inline AccessibilityTextMarkerRange::AccessibilityTextMarkerRange(PlatformTextMarkerRange) { }
+inline AccessibilityTextMarkerRange::AccessibilityTextMarkerRange(const AccessibilityTextMarkerRange&) { }
+inline AccessibilityTextMarkerRange::~AccessibilityTextMarkerRange() { }
+inline bool AccessibilityTextMarkerRange::isEqual(AccessibilityTextMarkerRange*) { return false; }
+inline PlatformTextMarkerRange AccessibilityTextMarkerRange::platformTextMarkerRange() const { return m_textMarkerRange; }
+#endif
+
+#endif // AccessibilityUIElement_h
diff --git a/Tools/DumpRenderTree/AccessibilityUIElement.cpp b/Tools/DumpRenderTree/AccessibilityUIElement.cpp
new file mode 100644
index 000000000..4bfc5bb84
--- /dev/null
+++ b/Tools/DumpRenderTree/AccessibilityUIElement.cpp
@@ -0,0 +1,1733 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if HAVE(ACCESSIBILITY)
+
+#include "AccessibilityUIElement.h"
+
+#include <JavaScriptCore/JSObjectRef.h>
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <limits.h>
+
+// Static Functions
+
+static inline AccessibilityUIElement* toAXElement(JSObjectRef object)
+{
+ // FIXME: We should ASSERT that it is the right class here.
+ return static_cast<AccessibilityUIElement*>(JSObjectGetPrivate(object));
+}
+
+static JSValueRef allAttributesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> attributes(Adopt, toAXElement(thisObject)->allAttributes());
+ return JSValueMakeString(context, attributes.get());
+}
+
+static JSValueRef attributesOfLinkedUIElementsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> linkedUIDescription(Adopt, toAXElement(thisObject)->attributesOfLinkedUIElements());
+ return JSValueMakeString(context, linkedUIDescription.get());
+}
+
+static JSValueRef attributesOfDocumentLinksCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> linkedUIDescription(Adopt, toAXElement(thisObject)->attributesOfDocumentLinks());
+ return JSValueMakeString(context, linkedUIDescription.get());
+}
+
+static JSValueRef attributesOfChildrenCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> childrenDescription(Adopt, toAXElement(thisObject)->attributesOfChildren());
+ return JSValueMakeString(context, childrenDescription.get());
+}
+
+static JSValueRef parameterizedAttributeNamesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> parameterizedAttributeNames(Adopt, toAXElement(thisObject)->parameterizedAttributeNames());
+ return JSValueMakeString(context, parameterizedAttributeNames.get());
+}
+
+static JSValueRef attributesOfColumnHeadersCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> attributesOfColumnHeaders(Adopt, toAXElement(thisObject)->attributesOfColumnHeaders());
+ return JSValueMakeString(context, attributesOfColumnHeaders.get());
+}
+
+static JSValueRef attributesOfRowHeadersCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> attributesOfRowHeaders(Adopt, toAXElement(thisObject)->attributesOfRowHeaders());
+ return JSValueMakeString(context, attributesOfRowHeaders.get());
+}
+
+static JSValueRef attributesOfColumnsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> attributesOfColumns(Adopt, toAXElement(thisObject)->attributesOfColumns());
+ return JSValueMakeString(context, attributesOfColumns.get());
+}
+
+static JSValueRef attributesOfRowsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> attributesOfRows(Adopt, toAXElement(thisObject)->attributesOfRows());
+ return JSValueMakeString(context, attributesOfRows.get());
+}
+
+static JSValueRef attributesOfVisibleCellsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> attributesOfVisibleCells(Adopt, toAXElement(thisObject)->attributesOfVisibleCells());
+ return JSValueMakeString(context, attributesOfVisibleCells.get());
+}
+
+static JSValueRef attributesOfHeaderCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> attributesOfHeader(Adopt, toAXElement(thisObject)->attributesOfHeader());
+ return JSValueMakeString(context, attributesOfHeader.get());
+}
+
+static JSValueRef indexInTableCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->indexInTable());
+}
+
+static JSValueRef rowIndexRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> rowIndexRange(Adopt, toAXElement(thisObject)->rowIndexRange());
+ return JSValueMakeString(context, rowIndexRange.get());
+}
+
+static JSValueRef columnIndexRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> columnIndexRange(Adopt, toAXElement(thisObject)->columnIndexRange());
+ return JSValueMakeString(context, columnIndexRange.get());
+}
+
+static JSValueRef lineForIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = -1;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ return JSValueMakeNumber(context, toAXElement(thisObject)->lineForIndex(indexNumber));
+}
+
+static JSValueRef rangeForLineCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = -1;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ JSRetainPtr<JSStringRef> rangeLine(Adopt, toAXElement(thisObject)->rangeForLine(indexNumber));
+ return JSValueMakeString(context, rangeLine.get());
+}
+
+static JSValueRef boundsForRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ unsigned location = UINT_MAX, length = 0;
+ if (argumentCount == 2) {
+ location = JSValueToNumber(context, arguments[0], exception);
+ length = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ JSRetainPtr<JSStringRef> boundsDescription(Adopt, toAXElement(thisObject)->boundsForRange(location, length));
+ return JSValueMakeString(context, boundsDescription.get());
+}
+
+static JSValueRef rangeForPositionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int x = 0, y = 0;
+ if (argumentCount == 2) {
+ x = JSValueToNumber(context, arguments[0], exception);
+ y = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ JSRetainPtr<JSStringRef> rangeDescription(Adopt, toAXElement(thisObject)->rangeForPosition(x, y));
+ return JSValueMakeString(context, rangeDescription.get());
+}
+
+static JSValueRef stringForRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ unsigned location = UINT_MAX, length = 0;
+ if (argumentCount == 2) {
+ location = JSValueToNumber(context, arguments[0], exception);
+ length = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ JSRetainPtr<JSStringRef> stringDescription(Adopt, toAXElement(thisObject)->stringForRange(location, length));
+ return JSValueMakeString(context, stringDescription.get());
+}
+
+static JSValueRef attributedStringForRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ unsigned location = UINT_MAX, length = 0;
+ if (argumentCount == 2) {
+ location = JSValueToNumber(context, arguments[0], exception);
+ length = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ JSRetainPtr<JSStringRef> stringDescription(Adopt, toAXElement(thisObject)->attributedStringForRange(location, length));
+ return JSValueMakeString(context, stringDescription.get());
+}
+
+static JSValueRef attributedStringRangeIsMisspelledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ unsigned location = UINT_MAX, length = 0;
+ if (argumentCount == 2) {
+ location = JSValueToNumber(context, arguments[0], exception);
+ length = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->attributedStringRangeIsMisspelled(location, length));
+}
+
+static JSValueRef uiElementCountForSearchPredicateCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityUIElement* startElement = nullptr;
+ bool isDirectionNext = true;
+ JSValueRef searchKey = nullptr;
+ JSRetainPtr<JSStringRef> searchText = nullptr;
+ bool visibleOnly = false;
+ bool immediateDescendantsOnly = false;
+ if (argumentCount >= 5 && argumentCount <= 6) {
+ if (JSValueIsObject(context, arguments[0]))
+ startElement = toAXElement(JSValueToObject(context, arguments[0], exception));
+
+ isDirectionNext = JSValueToBoolean(context, arguments[1]);
+
+ searchKey = arguments[2];
+
+ if (JSValueIsString(context, arguments[3]))
+ searchText.adopt(JSValueToStringCopy(context, arguments[3], exception));
+
+ visibleOnly = JSValueToBoolean(context, arguments[4]);
+
+ if (argumentCount == 6)
+ immediateDescendantsOnly = JSValueToBoolean(context, arguments[5]);
+ }
+
+ return JSValueMakeNumber(context, toAXElement(thisObject)->uiElementCountForSearchPredicate(context, startElement, isDirectionNext, searchKey, searchText.get(), visibleOnly, immediateDescendantsOnly));
+}
+
+static JSValueRef uiElementForSearchPredicateCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityUIElement* startElement = nullptr;
+ bool isDirectionNext = true;
+ JSValueRef searchKey = nullptr;
+ JSRetainPtr<JSStringRef> searchText = nullptr;
+ bool visibleOnly = false;
+ bool immediateDescendantsOnly = false;
+ if (argumentCount >= 5 && argumentCount <= 6) {
+ if (JSValueIsObject(context, arguments[0]))
+ startElement = toAXElement(JSValueToObject(context, arguments[0], exception));
+
+ isDirectionNext = JSValueToBoolean(context, arguments[1]);
+
+ searchKey = arguments[2];
+
+ if (JSValueIsString(context, arguments[3]))
+ searchText.adopt(JSValueToStringCopy(context, arguments[3], exception));
+
+ visibleOnly = JSValueToBoolean(context, arguments[4]);
+
+ if (argumentCount == 6)
+ immediateDescendantsOnly = JSValueToBoolean(context, arguments[5]);
+ }
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->uiElementForSearchPredicate(context, startElement, isDirectionNext, searchKey, searchText.get(), visibleOnly, immediateDescendantsOnly));
+}
+
+static JSValueRef selectTextWithCriteriaCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 2 || argumentCount > 4)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> ambiguityResolution(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ JSValueRef searchStrings = arguments[1];
+ JSStringRef replacementString = nullptr;
+ if (argumentCount == 3)
+ replacementString = JSValueToStringCopy(context, arguments[2], exception);
+ JSStringRef activityString = nullptr;
+ if (argumentCount == 4)
+ activityString = JSValueToStringCopy(context, arguments[3], exception);
+
+ JSRetainPtr<JSStringRef> result(Adopt, toAXElement(thisObject)->selectTextWithCriteria(context, ambiguityResolution.get(), searchStrings, replacementString, activityString));
+ if (replacementString)
+ JSStringRelease(replacementString);
+ if (activityString)
+ JSStringRelease(activityString);
+ return JSValueMakeString(context, result.get());
+}
+
+static JSValueRef indexOfChildCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 1)
+ return 0;
+
+ JSObjectRef otherElement = JSValueToObject(context, arguments[0], exception);
+ AccessibilityUIElement* childElement = toAXElement(otherElement);
+ return JSValueMakeNumber(context, (double)toAXElement(thisObject)->indexOfChild(childElement));
+}
+
+#if PLATFORM(IOS)
+
+static JSValueRef headerElementAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 1)
+ return 0;
+
+ unsigned index = JSValueToNumber(context, arguments[0], exception);
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->headerElementAtIndex(index));
+}
+
+static JSValueRef linkedElementCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->linkedElement());
+}
+
+static JSValueRef elementsForRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 2)
+ return 0;
+
+ unsigned location = JSValueToNumber(context, arguments[0], exception);
+ unsigned length = JSValueToNumber(context, arguments[1], exception);
+
+ Vector<AccessibilityUIElement> elements;
+ toAXElement(thisObject)->elementsForRange(location, length, elements);
+
+ JSValueRef arrayResult = JSObjectMakeArray(context, 0, 0, 0);
+ JSObjectRef arrayObj = JSValueToObject(context, arrayResult, 0);
+ unsigned elementsSize = elements.size();
+ for (unsigned k = 0; k < elementsSize; ++k)
+ JSObjectSetPropertyAtIndex(context, arrayObj, k, AccessibilityUIElement::makeJSAccessibilityUIElement(context, elements[k]), 0);
+
+ return arrayResult;
+}
+
+static JSValueRef increaseTextSelectionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->increaseTextSelection();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef scrollPageUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->scrollPageUp());
+}
+
+static JSValueRef scrollPageDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->scrollPageDown());
+}
+
+static JSValueRef scrollPageLeftCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->scrollPageLeft());
+}
+
+static JSValueRef scrollPageRightCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->scrollPageRight());
+}
+
+static JSValueRef decreaseTextSelectionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->decreaseTextSelection();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef assistiveTechnologySimulatedFocusCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->assistiveTechnologySimulatedFocus();
+ return JSValueMakeUndefined(context);
+}
+
+#endif
+
+static JSValueRef childAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = -1;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->getChildAtIndex(indexNumber));
+}
+
+static JSValueRef selectedChildAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = -1;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->selectedChildAtIndex(indexNumber));
+}
+
+static JSValueRef linkedUIElementAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = -1;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->linkedUIElementAtIndex(indexNumber));
+}
+
+static JSValueRef disclosedRowAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = 0;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->disclosedRowAtIndex(indexNumber));
+}
+
+static JSValueRef ariaOwnsElementAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = 0;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->ariaOwnsElementAtIndex(indexNumber));
+}
+
+static JSValueRef ariaFlowToElementAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = 0;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->ariaFlowToElementAtIndex(indexNumber));
+}
+
+static JSValueRef ariaControlsElementAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = 0;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->ariaControlsElementAtIndex(indexNumber));
+}
+
+static JSValueRef selectedRowAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = 0;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->selectedRowAtIndex(indexNumber));
+}
+
+static JSValueRef rowAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = 0;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->rowAtIndex(indexNumber));
+}
+
+static JSValueRef isEqualCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSObjectRef otherElement = 0;
+ if (argumentCount == 1)
+ otherElement = JSValueToObject(context, arguments[0], exception);
+ else
+ return JSValueMakeBoolean(context, false);
+
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isEqual(toAXElement(otherElement)));
+}
+
+static JSValueRef setValueCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> valueText = 0;
+ if (argumentCount == 1) {
+ if (JSValueIsString(context, arguments[0]))
+ valueText.adopt(JSValueToStringCopy(context, arguments[0], exception));
+ }
+
+ toAXElement(thisObject)->setValue(valueText.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setSelectedChildCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSObjectRef element = 0;
+ if (argumentCount == 1)
+ element = JSValueToObject(context, arguments[0], exception);
+
+ toAXElement(thisObject)->setSelectedChild(toAXElement(element));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef elementAtPointCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int x = 0;
+ int y = 0;
+ if (argumentCount == 2) {
+ x = JSValueToNumber(context, arguments[0], exception);
+ y = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->elementAtPoint(x, y));
+}
+
+static JSValueRef isAttributeSupportedCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSStringRef attribute = 0;
+ if (argumentCount == 1)
+ attribute = JSValueToStringCopy(context, arguments[0], exception);
+ JSValueRef result = JSValueMakeBoolean(context, toAXElement(thisObject)->isAttributeSupported(attribute));
+ if (attribute)
+ JSStringRelease(attribute);
+ return result;
+}
+
+static JSValueRef isAttributeSettableCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSStringRef attribute = 0;
+ if (argumentCount == 1)
+ attribute = JSValueToStringCopy(context, arguments[0], exception);
+ JSValueRef result = JSValueMakeBoolean(context, toAXElement(thisObject)->isAttributeSettable(attribute));
+ if (attribute)
+ JSStringRelease(attribute);
+ return result;
+}
+
+static JSValueRef isPressActionSupportedCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isPressActionSupported());
+}
+
+static JSValueRef isIncrementActionSupportedCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isIncrementActionSupported());
+}
+
+static JSValueRef isDecrementActionSupportedCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isDecrementActionSupported());
+}
+
+static JSValueRef boolAttributeValueCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSStringRef attribute = 0;
+ if (argumentCount == 1)
+ attribute = JSValueToStringCopy(context, arguments[0], exception);
+ bool val = toAXElement(thisObject)->boolAttributeValue(attribute);
+ JSValueRef result = JSValueMakeBoolean(context, val);
+ if (attribute)
+ JSStringRelease(attribute);
+ return result;
+}
+
+static JSValueRef setBoolAttributeValueCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSStringRef attribute = nullptr;
+ bool value = false;
+ if (argumentCount == 2) {
+ attribute = JSValueToStringCopy(context, arguments[0], exception);
+ value = JSValueToBoolean(context, arguments[1]);
+ }
+ toAXElement(thisObject)->setBoolAttributeValue(attribute, value);
+ if (attribute)
+ JSStringRelease(attribute);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef stringAttributeValueCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSStringRef attribute = 0;
+ if (argumentCount == 1)
+ attribute = JSValueToStringCopy(context, arguments[0], exception);
+ JSRetainPtr<JSStringRef> stringAttributeValue(Adopt, toAXElement(thisObject)->stringAttributeValue(attribute));
+ JSValueRef result = JSValueMakeString(context, stringAttributeValue.get());
+ if (attribute)
+ JSStringRelease(attribute);
+ return result;
+}
+
+static JSValueRef convertElementsToObjectArray(JSContextRef context, Vector<AccessibilityUIElement>& elements, JSValueRef* exception)
+{
+ JSValueRef arrayResult = JSObjectMakeArray(context, 0, 0, 0);
+ JSObjectRef arrayObj = JSValueToObject(context, arrayResult, 0);
+
+ size_t elementCount = elements.size();
+ for (size_t i = 0; i < elementCount; ++i)
+ JSObjectSetPropertyAtIndex(context, arrayObj, i, AccessibilityUIElement::makeJSAccessibilityUIElement(context, elements[i]), 0);
+
+ return arrayResult;
+}
+
+static JSValueRef columnHeadersCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ Vector<AccessibilityUIElement> elements;
+ toAXElement(thisObject)->columnHeaders(elements);
+ return convertElementsToObjectArray(context, elements, exception);
+}
+
+static JSValueRef rowHeadersCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ Vector<AccessibilityUIElement> elements;
+ toAXElement(thisObject)->rowHeaders(elements);
+ return convertElementsToObjectArray(context, elements, exception);
+}
+
+static JSValueRef uiElementArrayAttributeValueCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> attribute(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+
+ Vector<AccessibilityUIElement> elements;
+ toAXElement(thisObject)->uiElementArrayAttributeValue(attribute.get(), elements);
+ return convertElementsToObjectArray(context, elements, exception);
+}
+
+static JSValueRef uiElementAttributeValueCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> attribute;
+ if (argumentCount == 1)
+ attribute.adopt(JSValueToStringCopy(context, arguments[0], exception));
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->uiElementAttributeValue(attribute.get()));
+}
+
+static JSValueRef numberAttributeValueCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSStringRef attribute = 0;
+ if (argumentCount == 1)
+ attribute = JSValueToStringCopy(context, arguments[0], exception);
+ double val = toAXElement(thisObject)->numberAttributeValue(attribute);
+ JSValueRef result = JSValueMakeNumber(context, val);
+ if (attribute)
+ JSStringRelease(attribute);
+ return result;
+}
+
+static JSValueRef cellForColumnAndRowCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ unsigned column = 0, row = 0;
+ if (argumentCount == 2) {
+ column = JSValueToNumber(context, arguments[0], exception);
+ row = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->cellForColumnAndRow(column, row));
+}
+
+static JSValueRef titleUIElementCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->titleUIElement());
+}
+
+static JSValueRef parentElementCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->parentElement());
+}
+
+static JSValueRef disclosedByRowCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->disclosedByRow());
+}
+
+static JSValueRef setSelectedTextRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ unsigned location = UINT_MAX, length = 0;
+ if (argumentCount == 2) {
+ location = JSValueToNumber(context, arguments[0], exception);
+ length = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ toAXElement(thisObject)->setSelectedTextRange(location, length);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef incrementCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->increment();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef decrementCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->decrement();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef showMenuCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->showMenu();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef pressCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->press();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef scrollToMakeVisibleCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->scrollToMakeVisible();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef takeFocusCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->takeFocus();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef takeSelectionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->takeSelection();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef addSelectionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->addSelection();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef removeSelectionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->removeSelection();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef lineTextMarkerRangeForTextMarkerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarker* textMarker = nullptr;
+ if (argumentCount == 1)
+ textMarker = toTextMarker(JSValueToObject(context, arguments[0], exception));
+ return AccessibilityTextMarkerRange::makeJSAccessibilityTextMarkerRange(context, toAXElement(thisObject)->lineTextMarkerRangeForTextMarker(textMarker));
+}
+
+static JSValueRef textMarkerRangeForElementCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityUIElement* uiElement = 0;
+ if (argumentCount == 1)
+ uiElement = toAXElement(JSValueToObject(context, arguments[0], exception));
+
+ return AccessibilityTextMarkerRange::makeJSAccessibilityTextMarkerRange(context, toAXElement(thisObject)->textMarkerRangeForElement(uiElement));
+}
+
+static JSValueRef selectedTextMarkerRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return AccessibilityTextMarkerRange::makeJSAccessibilityTextMarkerRange(context, toAXElement(thisObject)->selectedTextMarkerRange());
+}
+
+static JSValueRef resetSelectedTextMarkerRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->resetSelectedTextMarkerRange();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef attributedStringForTextMarkerRangeContainsAttributeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarkerRange* markerRange = 0;
+ JSStringRef attribute = 0;
+ if (argumentCount == 2) {
+ attribute = JSValueToStringCopy(context, arguments[0], exception);
+ markerRange = toTextMarkerRange(JSValueToObject(context, arguments[1], exception));
+ }
+
+ JSValueRef result = JSValueMakeBoolean(context, toAXElement(thisObject)->attributedStringForTextMarkerRangeContainsAttribute(attribute, markerRange));
+ if (attribute)
+ JSStringRelease(attribute);
+
+ return result;
+}
+
+static JSValueRef indexForTextMarkerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarker* marker = 0;
+ if (argumentCount == 1)
+ marker = toTextMarker(JSValueToObject(context, arguments[0], exception));
+
+ return JSValueMakeNumber(context, toAXElement(thisObject)->indexForTextMarker(marker));
+}
+
+static JSValueRef isTextMarkerValidCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarker* marker = 0;
+ if (argumentCount == 1)
+ marker = toTextMarker(JSValueToObject(context, arguments[0], exception));
+
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isTextMarkerValid(marker));
+}
+
+static JSValueRef textMarkerForIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int textIndex = 0;
+ if (argumentCount == 1)
+ textIndex = JSValueToNumber(context, arguments[0], exception);
+
+ return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->textMarkerForIndex(textIndex));
+}
+
+static JSValueRef textMarkerRangeLengthCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarkerRange* range = 0;
+ if (argumentCount == 1)
+ range = toTextMarkerRange(JSValueToObject(context, arguments[0], exception));
+
+ return JSValueMakeNumber(context, (int)toAXElement(thisObject)->textMarkerRangeLength(range));
+}
+
+static JSValueRef nextTextMarkerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarker* marker = 0;
+ if (argumentCount == 1)
+ marker = toTextMarker(JSValueToObject(context, arguments[0], exception));
+
+ return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->nextTextMarker(marker));
+}
+
+static JSValueRef previousTextMarkerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarker* marker = 0;
+ if (argumentCount == 1)
+ marker = toTextMarker(JSValueToObject(context, arguments[0], exception));
+
+ return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->previousTextMarker(marker));
+}
+
+static JSValueRef stringForTextMarkerRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarkerRange* markerRange = 0;
+ if (argumentCount == 1)
+ markerRange = toTextMarkerRange(JSValueToObject(context, arguments[0], exception));
+
+ JSRetainPtr<JSStringRef> markerRangeString(Adopt, toAXElement(thisObject)->stringForTextMarkerRange(markerRange));
+ return JSValueMakeString(context, markerRangeString.get());
+}
+
+static JSValueRef endTextMarkerForBoundsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+ if (argumentCount == 4) {
+ x = JSValueToNumber(context, arguments[0], exception);
+ y = JSValueToNumber(context, arguments[1], exception);
+ width = JSValueToNumber(context, arguments[2], exception);
+ height = JSValueToNumber(context, arguments[3], exception);
+ }
+
+ return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->endTextMarkerForBounds(x, y, width, height));
+}
+
+static JSValueRef startTextMarkerForBoundsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+ if (argumentCount == 4) {
+ x = JSValueToNumber(context, arguments[0], exception);
+ y = JSValueToNumber(context, arguments[1], exception);
+ width = JSValueToNumber(context, arguments[2], exception);
+ height = JSValueToNumber(context, arguments[3], exception);
+ }
+
+ return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->startTextMarkerForBounds(x, y, width, height));
+}
+
+static JSValueRef textMarkerForPointCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int x = 0;
+ int y = 0;
+ if (argumentCount == 2) {
+ x = JSValueToNumber(context, arguments[0], exception);
+ y = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->textMarkerForPoint(x, y));
+}
+
+static JSValueRef textMarkerRangeForMarkersCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarker* startMarker = 0;
+ AccessibilityTextMarker* endMarker = 0;
+ if (argumentCount == 2) {
+ startMarker = toTextMarker(JSValueToObject(context, arguments[0], exception));
+ endMarker = toTextMarker(JSValueToObject(context, arguments[1], exception));
+ }
+
+ return AccessibilityTextMarkerRange::makeJSAccessibilityTextMarkerRange(context, toAXElement(thisObject)->textMarkerRangeForMarkers(startMarker, endMarker));
+}
+
+static JSValueRef startTextMarkerForTextMarkerRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarkerRange* markerRange = 0;
+ if (argumentCount == 1)
+ markerRange = toTextMarkerRange(JSValueToObject(context, arguments[0], exception));
+
+ return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->startTextMarkerForTextMarkerRange(markerRange));
+}
+
+static JSValueRef endTextMarkerForTextMarkerRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarkerRange* markerRange = 0;
+ if (argumentCount == 1)
+ markerRange = toTextMarkerRange(JSValueToObject(context, arguments[0], exception));
+
+ return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->endTextMarkerForTextMarkerRange(markerRange));
+}
+
+static JSValueRef accessibilityElementForTextMarkerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityTextMarker* marker = 0;
+ if (argumentCount == 1)
+ marker = toTextMarker(JSValueToObject(context, arguments[0], exception));
+
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->accessibilityElementForTextMarker(marker));
+}
+
+static JSValueRef startTextMarkerCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->startTextMarker());
+}
+
+static JSValueRef endTextMarkerCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->endTextMarker());
+}
+
+static JSValueRef setSelectedVisibleTextRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ AccessibilityUIElement* uiElement = toAXElement(thisObject);
+ AccessibilityTextMarkerRange* textMarkerRange = nullptr;
+ if (argumentCount == 1)
+ textMarkerRange = toTextMarkerRange(JSValueToObject(context, arguments[0], exception));
+
+ if (uiElement)
+ return JSValueMakeBoolean(context, uiElement->setSelectedVisibleTextRange(textMarkerRange));
+
+ return JSValueMakeBoolean(context, false);
+}
+
+// Static Value Getters
+
+static JSValueRef getARIADropEffectsCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> dropEffects(Adopt, toAXElement(thisObject)->ariaDropEffects());
+ return JSValueMakeString(context, dropEffects.get());
+}
+
+static JSValueRef getClassListCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> classList(Adopt, toAXElement(thisObject)->classList());
+ return JSValueMakeString(context, classList.get());
+}
+
+static JSValueRef getARIAIsGrabbedCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->ariaIsGrabbed());
+}
+
+static JSValueRef getIsValidCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ AccessibilityUIElement* uiElement = toAXElement(thisObject);
+ if (!uiElement->platformUIElement())
+ return JSValueMakeBoolean(context, false);
+
+ // There might be other platform logic that one could check here...
+
+ return JSValueMakeBoolean(context, true);
+}
+
+static JSValueRef getRoleCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> role(Adopt, toAXElement(thisObject)->role());
+ return JSValueMakeString(context, role.get());
+}
+
+static JSValueRef getSubroleCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> role(Adopt, toAXElement(thisObject)->subrole());
+ return JSValueMakeString(context, role.get());
+}
+
+static JSValueRef getRoleDescriptionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> roleDesc(Adopt, toAXElement(thisObject)->roleDescription());
+ return JSValueMakeString(context, roleDesc.get());
+}
+
+static JSValueRef getComputedRoleStringCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> compRole(Adopt, toAXElement(thisObject)->computedRoleString());
+ return JSValueMakeString(context, compRole.get());
+}
+
+static JSValueRef getTitleCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> title(Adopt, toAXElement(thisObject)->title());
+ return JSValueMakeString(context, title.get());
+}
+
+static JSValueRef getDescriptionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> description(Adopt, toAXElement(thisObject)->description());
+ return JSValueMakeString(context, description.get());
+}
+
+static JSValueRef getStringValueCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> stringValue(Adopt, toAXElement(thisObject)->stringValue());
+ return JSValueMakeString(context, stringValue.get());
+}
+
+static JSValueRef getLanguageCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> language(Adopt, toAXElement(thisObject)->language());
+ return JSValueMakeString(context, language.get());
+}
+
+static JSValueRef getHelpTextCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> language(Adopt, toAXElement(thisObject)->helpText());
+ return JSValueMakeString(context, language.get());
+}
+
+static JSValueRef getOrientationCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> orientation(Adopt, toAXElement(thisObject)->orientation());
+ return JSValueMakeString(context, orientation.get());
+}
+
+static JSValueRef getChildrenCountCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->childrenCount());
+}
+
+static JSValueRef rowCountCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->rowCount());
+}
+
+static JSValueRef columnCountCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->columnCount());
+}
+
+static JSValueRef getXCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->x());
+}
+
+static JSValueRef getYCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->y());
+}
+
+static JSValueRef getWidthCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->width());
+}
+
+static JSValueRef getHeightCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->height());
+}
+
+static JSValueRef getClickPointXCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->clickPointX());
+}
+
+static JSValueRef getClickPointYCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->clickPointY());
+}
+
+static JSValueRef getIntValueCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->intValue());
+}
+
+static JSValueRef getMinValueCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->minValue());
+}
+
+static JSValueRef getMaxValueCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->maxValue());
+}
+
+static JSValueRef getInsertionPointLineNumberCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->insertionPointLineNumber());
+}
+
+static JSValueRef getPathDescriptionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> pathDescription(Adopt, toAXElement(thisObject)->pathDescription());
+ return JSValueMakeString(context, pathDescription.get());
+}
+
+static JSValueRef getSelectedTextRangeCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> selectedTextRange(Adopt, toAXElement(thisObject)->selectedTextRange());
+ return JSValueMakeString(context, selectedTextRange.get());
+}
+
+static JSValueRef getIsEnabledCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isEnabled());
+}
+
+static JSValueRef getIsRequiredCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isRequired());
+}
+
+static JSValueRef getIsFocusedCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isFocused());
+}
+
+static JSValueRef getIsFocusableCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isFocusable());
+}
+
+static JSValueRef getIsSelectedCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isSelected());
+}
+
+static JSValueRef getIsSelectableCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isSelectable());
+}
+
+static JSValueRef getIsMultiSelectableCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isMultiSelectable());
+}
+
+static JSValueRef getIsSelectedOptionActiveCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isSelectedOptionActive());
+}
+
+static JSValueRef getIsExpandedCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isExpanded());
+}
+
+static JSValueRef getIsCheckedCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isChecked());
+}
+
+static JSValueRef getIsIndeterminate(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isIndeterminate());
+}
+
+static JSValueRef getIsVisibleCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isVisible());
+}
+
+static JSValueRef getIsOffScreenCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isOffScreen());
+}
+
+static JSValueRef getIsCollapsedCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isCollapsed());
+}
+
+static JSValueRef isIgnoredCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->isIgnored());
+}
+
+static JSValueRef speakCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ JSRetainPtr<JSStringRef> speakString(Adopt, toAXElement(thisObject)->speak());
+ return JSValueMakeString(context, speakString.get());
+}
+
+static JSValueRef selectedChildrenCountCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->selectedChildrenCount());
+}
+
+static JSValueRef horizontalScrollbarCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->horizontalScrollbar());
+}
+
+static JSValueRef verticalScrollbarCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->verticalScrollbar());
+}
+
+static JSValueRef getHasPopupCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeBoolean(context, toAXElement(thisObject)->hasPopup());
+}
+
+static JSValueRef hierarchicalLevelCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->hierarchicalLevel());
+}
+
+static JSValueRef getValueDescriptionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> valueDescription(Adopt, toAXElement(thisObject)->valueDescription());
+ return JSValueMakeString(context, valueDescription.get());
+}
+
+static JSValueRef getAccessibilityValueCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> accessibilityValue(Adopt, toAXElement(thisObject)->accessibilityValue());
+ return JSValueMakeString(context, accessibilityValue.get());
+}
+
+static JSValueRef getDocumentEncodingCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> documentEncoding(Adopt, toAXElement(thisObject)->documentEncoding());
+ return JSValueMakeString(context, documentEncoding.get());
+}
+
+static JSValueRef getDocumentURICallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> documentURI(Adopt, toAXElement(thisObject)->documentURI());
+ return JSValueMakeString(context, documentURI.get());
+}
+
+static JSValueRef getURLCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> url(Adopt, toAXElement(thisObject)->url());
+ return JSValueMakeString(context, url.get());
+}
+
+static JSValueRef addNotificationListenerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 1)
+ return JSValueMakeBoolean(context, false);
+
+ JSObjectRef callback = JSValueToObject(context, arguments[0], exception);
+ bool succeeded = toAXElement(thisObject)->addNotificationListener(callback);
+ return JSValueMakeBoolean(context, succeeded);
+}
+
+static JSValueRef removeNotificationListenerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->removeNotificationListener();
+ return JSValueMakeUndefined(context);
+}
+
+#if PLATFORM(GTK) || PLATFORM(EFL)
+static JSValueRef characterAtOffsetCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int offset = -1;
+ if (argumentCount == 1)
+ offset = JSValueToNumber(context, arguments[0], exception);
+
+ JSRetainPtr<JSStringRef> characterAtOffset(Adopt, toAXElement(thisObject)->characterAtOffset(offset));
+ return JSValueMakeString(context, characterAtOffset.get());
+}
+
+static JSValueRef wordAtOffsetCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int offset = -1;
+ if (argumentCount == 1)
+ offset = JSValueToNumber(context, arguments[0], exception);
+
+ JSRetainPtr<JSStringRef> wordAtOffset(Adopt, toAXElement(thisObject)->wordAtOffset(offset));
+ return JSValueMakeString(context, wordAtOffset.get());
+}
+
+static JSValueRef lineAtOffsetCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int offset = -1;
+ if (argumentCount == 1)
+ offset = JSValueToNumber(context, arguments[0], exception);
+
+ JSRetainPtr<JSStringRef> lineAtOffset(Adopt, toAXElement(thisObject)->lineAtOffset(offset));
+ return JSValueMakeString(context, lineAtOffset.get());
+}
+
+static JSValueRef sentenceAtOffsetCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int offset = -1;
+ if (argumentCount == 1)
+ offset = JSValueToNumber(context, arguments[0], exception);
+
+ JSRetainPtr<JSStringRef> sentenceAtOffset(Adopt, toAXElement(thisObject)->sentenceAtOffset(offset));
+ return JSValueMakeString(context, sentenceAtOffset.get());
+}
+
+static JSValueRef setSelectedChildAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = -1;
+ if (argumentCount == 1);
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ if (indexNumber >= 0)
+ toAXElement(thisObject)->setSelectedChildAtIndex(indexNumber);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef removeSelectionAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int indexNumber = -1;
+ if (argumentCount == 1)
+ indexNumber = JSValueToNumber(context, arguments[0], exception);
+
+ if (indexNumber >= 0)
+ toAXElement(thisObject)->removeSelectionAtIndex(indexNumber);
+ return JSValueMakeUndefined(context);
+}
+
+#elif PLATFORM(IOS)
+
+static JSValueRef stringForSelectionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> labelString(Adopt, toAXElement(thisObject)->stringForSelection());
+ return JSValueMakeString(context, labelString.get());
+}
+
+static JSValueRef getIdentifierCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> valueString(Adopt, toAXElement(thisObject)->identifier());
+ return JSValueMakeString(context, valueString.get());
+}
+
+
+static JSValueRef getTraitsCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> valueString(Adopt, toAXElement(thisObject)->traits());
+ return JSValueMakeString(context, valueString.get());
+}
+
+static JSValueRef getElementTextPositionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->elementTextPosition());
+}
+
+static JSValueRef getElementTextLengthCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->elementTextLength());
+}
+
+
+
+#endif // PLATFORM(IOS)
+
+#if PLATFORM(MAC) && !PLATFORM(IOS)
+static JSValueRef supportedActionsCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> valueString(Adopt, toAXElement(thisObject)->supportedActions());
+ return JSValueMakeString(context, valueString.get());
+}
+
+static JSValueRef mathPostscriptsDescriptionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> valueString(Adopt, toAXElement(thisObject)->mathPostscriptsDescription());
+ return JSValueMakeString(context, valueString.get());
+}
+
+static JSValueRef mathPrescriptsDescriptionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> valueString(Adopt, toAXElement(thisObject)->mathPrescriptsDescription());
+ return JSValueMakeString(context, valueString.get());
+}
+
+#endif
+
+// Implementation
+
+// Unsupported methods on various platforms.
+#if !PLATFORM(MAC) || PLATFORM(IOS)
+JSStringRef AccessibilityUIElement::speak() { return 0; }
+JSStringRef AccessibilityUIElement::rangeForLine(int line) { return 0; }
+JSStringRef AccessibilityUIElement::rangeForPosition(int, int) { return 0; }
+void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement*) const { }
+AccessibilityUIElement AccessibilityUIElement::horizontalScrollbar() const { return 0; }
+AccessibilityUIElement AccessibilityUIElement::verticalScrollbar() const { return 0; }
+AccessibilityUIElement AccessibilityUIElement::uiElementAttributeValue(JSStringRef) const { return 0; }
+#endif
+
+#if !PLATFORM(MAC) && !PLATFORM(IOS)
+JSStringRef AccessibilityUIElement::pathDescription() const { return 0; }
+void AccessibilityUIElement::setValue(JSStringRef) { }
+#endif
+
+#if !PLATFORM(COCOA)
+void AccessibilityUIElement::uiElementArrayAttributeValue(JSStringRef, Vector<AccessibilityUIElement>&) const { }
+#endif
+
+#if !PLATFORM(WIN)
+bool AccessibilityUIElement::isEqual(AccessibilityUIElement* otherElement)
+{
+ if (!otherElement)
+ return false;
+ return platformUIElement() == otherElement->platformUIElement();
+}
+#endif
+
+#if !PLATFORM(MAC)
+void AccessibilityUIElement::setBoolAttributeValue(JSStringRef, bool) { }
+#endif
+
+#if !SUPPORTS_AX_TEXTMARKERS
+
+AccessibilityTextMarkerRange AccessibilityUIElement::lineTextMarkerRangeForTextMarker(AccessibilityTextMarker*)
+{
+ return nullptr;
+}
+
+AccessibilityTextMarkerRange AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement*)
+{
+ return 0;
+}
+
+AccessibilityTextMarkerRange AccessibilityUIElement::selectedTextMarkerRange()
+{
+ return nullptr;
+}
+
+void AccessibilityUIElement::resetSelectedTextMarkerRange()
+{
+}
+
+int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange*)
+{
+ return 0;
+}
+
+AccessibilityTextMarkerRange AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker*, AccessibilityTextMarker*)
+{
+ return 0;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange*)
+{
+ return 0;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange*)
+{
+ return 0;
+}
+
+AccessibilityUIElement AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker*)
+{
+ return 0;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height)
+{
+ return 0;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
+{
+ return 0;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::textMarkerForPoint(int x, int y)
+{
+ return 0;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker*)
+{
+ return 0;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker*)
+{
+ return 0;
+}
+
+JSStringRef AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange*)
+{
+ return 0;
+}
+
+bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef, AccessibilityTextMarkerRange*)
+{
+ return false;
+}
+
+int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker*)
+{
+ return -1;
+}
+
+bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker*)
+{
+ return false;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::textMarkerForIndex(int)
+{
+ return 0;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::startTextMarker()
+{
+ return nullptr;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::endTextMarker()
+{
+ return nullptr;
+}
+
+bool AccessibilityUIElement::setSelectedVisibleTextRange(AccessibilityTextMarkerRange*)
+{
+ return false;
+}
+
+#endif
+
+// Destruction
+
+static void finalize(JSObjectRef thisObject)
+{
+ delete toAXElement(thisObject);
+}
+
+// Object Creation
+
+JSObjectRef AccessibilityUIElement::makeJSAccessibilityUIElement(JSContextRef context, const AccessibilityUIElement& element)
+{
+ if (!element.platformUIElement())
+ return nullptr;
+
+ return JSObjectMake(context, AccessibilityUIElement::getJSClass(), new AccessibilityUIElement(element));
+}
+
+JSClassRef AccessibilityUIElement::getJSClass()
+{
+ static JSStaticValue staticValues[] = {
+ { "accessibilityValue", getAccessibilityValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "role", getRoleCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "subrole", getSubroleCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "roleDescription", getRoleDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "computedRoleString", getComputedRoleStringCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "title", getTitleCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "description", getDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "language", getLanguageCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "helpText", getHelpTextCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "stringValue", getStringValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "x", getXCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "y", getYCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "width", getWidthCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "height", getHeightCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "clickPointX", getClickPointXCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "clickPointY", getClickPointYCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "intValue", getIntValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "minValue", getMinValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "maxValue", getMaxValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "pathDescription", getPathDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "childrenCount", getChildrenCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "rowCount", rowCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "columnCount", columnCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "insertionPointLineNumber", getInsertionPointLineNumberCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "selectedTextRange", getSelectedTextRangeCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isEnabled", getIsEnabledCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isRequired", getIsRequiredCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isFocused", getIsFocusedCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isFocusable", getIsFocusableCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isSelected", getIsSelectedCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isSelectable", getIsSelectableCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isMultiSelectable", getIsMultiSelectableCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isSelectedOptionActive", getIsSelectedOptionActiveCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isExpanded", getIsExpandedCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isChecked", getIsCheckedCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isIndeterminate", getIsIndeterminate, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isVisible", getIsVisibleCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isOffScreen", getIsOffScreenCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isCollapsed", getIsCollapsedCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "hasPopup", getHasPopupCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "valueDescription", getValueDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "hierarchicalLevel", hierarchicalLevelCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "documentEncoding", getDocumentEncodingCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "documentURI", getDocumentURICallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "url", getURLCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isValid", getIsValidCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "orientation", getOrientationCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "ariaIsGrabbed", getARIAIsGrabbedCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "ariaDropEffects", getARIADropEffectsCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "classList", getClassListCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isIgnored", isIgnoredCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "speak", speakCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "selectedChildrenCount", selectedChildrenCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "horizontalScrollbar", horizontalScrollbarCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "verticalScrollbar", verticalScrollbarCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "startTextMarker", startTextMarkerCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "endTextMarker", endTextMarkerCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+#if PLATFORM(IOS)
+ { "identifier", getIdentifierCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "traits", getTraitsCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "elementTextPosition", getElementTextPositionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "elementTextLength", getElementTextLengthCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "stringForSelection", stringForSelectionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+#endif // PLATFORM(IOS)
+#if PLATFORM(MAC) && !PLATFORM(IOS)
+ { "supportedActions", supportedActionsCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "mathPostscriptsDescription", mathPostscriptsDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "mathPrescriptsDescription", mathPrescriptsDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+#endif
+ { 0, 0, 0, 0 }
+ };
+
+ static JSStaticFunction staticFunctions[] = {
+ { "allAttributes", allAttributesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributesOfLinkedUIElements", attributesOfLinkedUIElementsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributesOfDocumentLinks", attributesOfDocumentLinksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributesOfChildren", attributesOfChildrenCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "parameterizedAttributeNames", parameterizedAttributeNamesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "lineForIndex", lineForIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "rangeForLine", rangeForLineCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "boundsForRange", boundsForRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "rangeForPosition", rangeForPositionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "stringForRange", stringForRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributedStringForRange", attributedStringForRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributedStringRangeIsMisspelled", attributedStringRangeIsMisspelledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "uiElementCountForSearchPredicate", uiElementCountForSearchPredicateCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "uiElementForSearchPredicate", uiElementForSearchPredicateCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "selectTextWithCriteria", selectTextWithCriteriaCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "childAtIndex", childAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "linkedUIElementAtIndex", linkedUIElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "indexOfChild", indexOfChildCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "elementAtPoint", elementAtPointCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributesOfColumnHeaders", attributesOfColumnHeadersCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributesOfRowHeaders", attributesOfRowHeadersCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributesOfColumns", attributesOfColumnsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributesOfRows", attributesOfRowsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributesOfVisibleCells", attributesOfVisibleCellsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributesOfHeader", attributesOfHeaderCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "indexInTable", indexInTableCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "rowIndexRange", rowIndexRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "columnIndexRange", columnIndexRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "cellForColumnAndRow", cellForColumnAndRowCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "titleUIElement", titleUIElementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setSelectedTextRange", setSelectedTextRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "stringAttributeValue", stringAttributeValueCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "uiElementArrayAttributeValue", uiElementArrayAttributeValueCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "rowHeaders", rowHeadersCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "columnHeaders", columnHeadersCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "uiElementAttributeValue", uiElementAttributeValueCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "numberAttributeValue", numberAttributeValueCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "boolAttributeValue", boolAttributeValueCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setBoolAttributeValue", setBoolAttributeValueCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isAttributeSupported", isAttributeSupportedCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isAttributeSettable", isAttributeSettableCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isPressActionSupported", isPressActionSupportedCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isIncrementActionSupported", isIncrementActionSupportedCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isDecrementActionSupported", isDecrementActionSupportedCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "parentElement", parentElementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "disclosedByRow", disclosedByRowCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "increment", incrementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "decrement", decrementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "showMenu", showMenuCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "press", pressCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "disclosedRowAtIndex", disclosedRowAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "ariaOwnsElementAtIndex", ariaOwnsElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "ariaFlowToElementAtIndex", ariaFlowToElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "ariaControlsElementAtIndex", ariaControlsElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "selectedRowAtIndex", selectedRowAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "rowAtIndex", rowAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isEqual", isEqualCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "addNotificationListener", addNotificationListenerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "removeNotificationListener", removeNotificationListenerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "takeFocus", takeFocusCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "takeSelection", takeSelectionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "addSelection", addSelectionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "removeSelection", removeSelectionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "lineTextMarkerRangeForTextMarker", lineTextMarkerRangeForTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "textMarkerRangeForElement", textMarkerRangeForElementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "selectedTextMarkerRange", selectedTextMarkerRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "resetSelectedTextMarkerRange", resetSelectedTextMarkerRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "attributedStringForTextMarkerRangeContainsAttribute", attributedStringForTextMarkerRangeContainsAttributeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "indexForTextMarker", indexForTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isTextMarkerValid", isTextMarkerValidCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "textMarkerRangeForMarkers", textMarkerRangeForMarkersCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "textMarkerForIndex", textMarkerForIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "startTextMarkerForTextMarkerRange", startTextMarkerForTextMarkerRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "endTextMarkerForTextMarkerRange", endTextMarkerForTextMarkerRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "accessibilityElementForTextMarker", accessibilityElementForTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "textMarkerRangeLength", textMarkerRangeLengthCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "endTextMarkerForBounds", endTextMarkerForBoundsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "startTextMarkerForBounds", startTextMarkerForBoundsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "textMarkerForPoint", textMarkerForPointCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "nextTextMarker", nextTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "previousTextMarker", previousTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "stringForTextMarkerRange", stringForTextMarkerRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setSelectedChild", setSelectedChildCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setValue", setValueCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setSelectedVisibleTextRange", setSelectedVisibleTextRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "selectedChildAtIndex", selectedChildAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "scrollToMakeVisible", scrollToMakeVisibleCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ { "characterAtOffset", characterAtOffsetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "wordAtOffset", wordAtOffsetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "lineAtOffset", lineAtOffsetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "sentenceAtOffset", sentenceAtOffsetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setSelectedChildAtIndex", setSelectedChildAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "removeSelectionAtIndex", removeSelectionAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+#elif PLATFORM(IOS)
+ { "linkedElement", linkedElementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "headerElementAtIndex", headerElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "elementsForRange", elementsForRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "increaseTextSelection", increaseTextSelectionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "decreaseTextSelection", decreaseTextSelectionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "scrollPageUp", scrollPageUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "scrollPageDown", scrollPageDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "scrollPageLeft", scrollPageLeftCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "scrollPageRight", scrollPageRightCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "assistiveTechnologySimulatedFocus", assistiveTechnologySimulatedFocusCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+#endif
+ { 0, 0, 0 }
+ };
+
+ static JSClassDefinition classDefinition = {
+ 0, kJSClassAttributeNone, "AccessibilityUIElement", 0, staticValues, staticFunctions,
+ 0, finalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ static JSClassRef accessibilityUIElementClass = JSClassCreate(&classDefinition);
+ return accessibilityUIElementClass;
+}
+#endif
diff --git a/Tools/DumpRenderTree/AccessibilityUIElement.h b/Tools/DumpRenderTree/AccessibilityUIElement.h
new file mode 100644
index 000000000..0db3e262e
--- /dev/null
+++ b/Tools/DumpRenderTree/AccessibilityUIElement.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityUIElement_h
+#define AccessibilityUIElement_h
+
+#include "AccessibilityTextMarker.h"
+#include <JavaScriptCore/JSObjectRef.h>
+#include <wtf/Platform.h>
+#include <wtf/Vector.h>
+
+#if PLATFORM(COCOA)
+#ifdef __OBJC__
+typedef id PlatformUIElement;
+#else
+typedef struct objc_object* PlatformUIElement;
+#endif
+#elif PLATFORM(WIN)
+#undef _WINSOCKAPI_
+#define _WINSOCKAPI_ // Prevent inclusion of winsock.h in windows.h
+
+#include <WebCore/COMPtr.h>
+#include <oleacc.h>
+
+typedef COMPtr<IAccessible> PlatformUIElement;
+#elif HAVE(ACCESSIBILITY) && (PLATFORM(GTK) || PLATFORM(EFL))
+#include "AccessibilityNotificationHandlerAtk.h"
+#include <atk/atk.h>
+typedef AtkObject* PlatformUIElement;
+#else
+typedef void* PlatformUIElement;
+#endif
+
+#if PLATFORM(COCOA)
+#ifdef __OBJC__
+typedef id NotificationHandler;
+#else
+typedef struct objc_object* NotificationHandler;
+#endif
+#endif
+
+class AccessibilityUIElement {
+public:
+ AccessibilityUIElement(PlatformUIElement);
+ AccessibilityUIElement(const AccessibilityUIElement&);
+ ~AccessibilityUIElement();
+
+ PlatformUIElement platformUIElement() const { return m_element; }
+
+ static JSObjectRef makeJSAccessibilityUIElement(JSContextRef, const AccessibilityUIElement&);
+
+ bool isEqual(AccessibilityUIElement* otherElement);
+
+ void getLinkedUIElements(Vector<AccessibilityUIElement>&);
+ void getDocumentLinks(Vector<AccessibilityUIElement>&);
+ void getChildren(Vector<AccessibilityUIElement>&);
+ void getChildrenWithRange(Vector<AccessibilityUIElement>&, unsigned location, unsigned length);
+
+ AccessibilityUIElement elementAtPoint(int x, int y);
+ AccessibilityUIElement getChildAtIndex(unsigned);
+ unsigned indexOfChild(AccessibilityUIElement*);
+ int childrenCount();
+ AccessibilityUIElement titleUIElement();
+ AccessibilityUIElement parentElement();
+
+ void takeFocus();
+ void takeSelection();
+ void addSelection();
+ void removeSelection();
+
+ // Methods - platform-independent implementations
+ JSStringRef allAttributes();
+ JSStringRef attributesOfLinkedUIElements();
+ AccessibilityUIElement linkedUIElementAtIndex(unsigned);
+
+ JSStringRef attributesOfDocumentLinks();
+ JSStringRef attributesOfChildren();
+ JSStringRef parameterizedAttributeNames();
+ void increment();
+ void decrement();
+ void showMenu();
+ void press();
+
+ // Attributes - platform-independent implementations
+ JSStringRef stringAttributeValue(JSStringRef attribute);
+ double numberAttributeValue(JSStringRef attribute);
+ void uiElementArrayAttributeValue(JSStringRef attribute, Vector<AccessibilityUIElement>& elements) const;
+ AccessibilityUIElement uiElementAttributeValue(JSStringRef attribute) const;
+ bool boolAttributeValue(JSStringRef attribute);
+ void setBoolAttributeValue(JSStringRef attribute, bool value);
+ bool isAttributeSupported(JSStringRef attribute);
+ bool isAttributeSettable(JSStringRef attribute);
+ bool isPressActionSupported();
+ bool isIncrementActionSupported();
+ bool isDecrementActionSupported();
+ JSStringRef role();
+ JSStringRef subrole();
+ JSStringRef roleDescription();
+ JSStringRef computedRoleString();
+ JSStringRef title();
+ JSStringRef description();
+ JSStringRef language();
+ JSStringRef stringValue();
+ JSStringRef accessibilityValue() const;
+ void setValue(JSStringRef);
+ JSStringRef helpText() const;
+ JSStringRef orientation() const;
+ double x();
+ double y();
+ double width();
+ double height();
+ double intValue() const;
+ double minValue();
+ double maxValue();
+ JSStringRef pathDescription() const;
+ JSStringRef valueDescription();
+ int insertionPointLineNumber();
+ JSStringRef selectedTextRange();
+ bool isEnabled();
+ bool isRequired() const;
+
+ bool isFocused() const;
+ bool isFocusable() const;
+ bool isSelected() const;
+ bool isSelectable() const;
+ bool isMultiSelectable() const;
+ bool isSelectedOptionActive() const;
+ void setSelectedChild(AccessibilityUIElement*) const;
+ unsigned selectedChildrenCount() const;
+ AccessibilityUIElement selectedChildAtIndex(unsigned) const;
+ void setSelectedChildAtIndex(unsigned) const;
+ void removeSelectionAtIndex(unsigned) const;
+
+ bool isExpanded() const;
+ bool isChecked() const;
+ bool isVisible() const;
+ bool isOffScreen() const;
+ bool isCollapsed() const;
+ bool isIgnored() const;
+ bool isIndeterminate() const;
+ bool hasPopup() const;
+ int hierarchicalLevel() const;
+ double clickPointX();
+ double clickPointY();
+ JSStringRef documentEncoding();
+ JSStringRef documentURI();
+ JSStringRef url();
+ JSStringRef classList() const;
+
+ // CSS3-speech properties.
+ JSStringRef speak();
+
+ // Table-specific attributes
+ JSStringRef attributesOfColumnHeaders();
+ JSStringRef attributesOfRowHeaders();
+ JSStringRef attributesOfColumns();
+ JSStringRef attributesOfRows();
+ JSStringRef attributesOfVisibleCells();
+ JSStringRef attributesOfHeader();
+ int indexInTable();
+ JSStringRef rowIndexRange();
+ JSStringRef columnIndexRange();
+ int rowCount();
+ int columnCount();
+ void rowHeaders(Vector<AccessibilityUIElement>& elements) const;
+ void columnHeaders(Vector<AccessibilityUIElement>& elements) const;
+
+ // Tree/Outline specific attributes
+ AccessibilityUIElement selectedRowAtIndex(unsigned);
+ AccessibilityUIElement disclosedByRow();
+ AccessibilityUIElement disclosedRowAtIndex(unsigned);
+ AccessibilityUIElement rowAtIndex(unsigned);
+
+ // ARIA specific
+ AccessibilityUIElement ariaOwnsElementAtIndex(unsigned);
+ AccessibilityUIElement ariaFlowToElementAtIndex(unsigned);
+ AccessibilityUIElement ariaControlsElementAtIndex(unsigned);
+
+ // ARIA Drag and Drop
+ bool ariaIsGrabbed() const;
+ // A space concatentated string of all the drop effects.
+ JSStringRef ariaDropEffects() const;
+
+ // Parameterized attributes
+ int lineForIndex(int);
+ JSStringRef rangeForLine(int);
+ JSStringRef rangeForPosition(int x, int y);
+ JSStringRef boundsForRange(unsigned location, unsigned length);
+ void setSelectedTextRange(unsigned location, unsigned length);
+ JSStringRef stringForRange(unsigned location, unsigned length);
+ JSStringRef attributedStringForRange(unsigned location, unsigned length);
+ bool attributedStringRangeIsMisspelled(unsigned location, unsigned length);
+ unsigned uiElementCountForSearchPredicate(JSContextRef, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly);
+ AccessibilityUIElement uiElementForSearchPredicate(JSContextRef, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly);
+ JSStringRef selectTextWithCriteria(JSContextRef, JSStringRef ambiguityResolution, JSValueRef searchStrings, JSStringRef replacementString, JSStringRef activity);
+#if PLATFORM(IOS)
+ void elementsForRange(unsigned location, unsigned length, Vector<AccessibilityUIElement>& elements);
+ JSStringRef stringForSelection();
+ void increaseTextSelection();
+ void decreaseTextSelection();
+ AccessibilityUIElement linkedElement();
+
+ bool scrollPageUp();
+ bool scrollPageDown();
+ bool scrollPageLeft();
+ bool scrollPageRight();
+#endif
+
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ // Text-specific
+ JSStringRef characterAtOffset(int offset);
+ JSStringRef wordAtOffset(int offset);
+ JSStringRef lineAtOffset(int offset);
+ JSStringRef sentenceAtOffset(int offset);
+#endif
+
+ // Table-specific
+ AccessibilityUIElement cellForColumnAndRow(unsigned column, unsigned row);
+
+ // Scrollarea-specific
+ AccessibilityUIElement horizontalScrollbar() const;
+ AccessibilityUIElement verticalScrollbar() const;
+
+ // Text markers.
+ AccessibilityTextMarkerRange lineTextMarkerRangeForTextMarker(AccessibilityTextMarker*);
+ AccessibilityTextMarkerRange textMarkerRangeForElement(AccessibilityUIElement*);
+ AccessibilityTextMarkerRange textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker);
+ AccessibilityTextMarker startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange*);
+ AccessibilityTextMarker endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange*);
+ AccessibilityTextMarker endTextMarkerForBounds(int x, int y, int width, int height);
+ AccessibilityTextMarker startTextMarkerForBounds(int x, int y, int width, int height);
+ AccessibilityTextMarker textMarkerForPoint(int x, int y);
+ AccessibilityTextMarker previousTextMarker(AccessibilityTextMarker*);
+ AccessibilityTextMarker nextTextMarker(AccessibilityTextMarker*);
+ AccessibilityUIElement accessibilityElementForTextMarker(AccessibilityTextMarker*);
+ AccessibilityTextMarker startTextMarker();
+ AccessibilityTextMarker endTextMarker();
+ AccessibilityTextMarkerRange selectedTextMarkerRange();
+ void resetSelectedTextMarkerRange();
+ bool setSelectedVisibleTextRange(AccessibilityTextMarkerRange*);
+
+ JSStringRef stringForTextMarkerRange(AccessibilityTextMarkerRange*);
+ int textMarkerRangeLength(AccessibilityTextMarkerRange*);
+ bool attributedStringForTextMarkerRangeContainsAttribute(JSStringRef, AccessibilityTextMarkerRange*);
+ int indexForTextMarker(AccessibilityTextMarker*);
+ bool isTextMarkerValid(AccessibilityTextMarker*);
+ AccessibilityTextMarker textMarkerForIndex(int);
+
+ void scrollToMakeVisible();
+ void scrollToMakeVisibleWithSubFocus(int x, int y, int width, int height);
+ void scrollToGlobalPoint(int x, int y);
+
+ // Notifications
+ // Function callback should take one argument, the name of the notification.
+ bool addNotificationListener(JSObjectRef functionCallback);
+ // Make sure you call remove, because you can't rely on objects being deallocated in a timely fashion.
+ void removeNotificationListener();
+
+#if PLATFORM(IOS)
+ JSStringRef traits();
+ JSStringRef identifier();
+ int elementTextPosition();
+ int elementTextLength();
+ AccessibilityUIElement headerElementAtIndex(unsigned);
+ // This will simulate the accessibilityDidBecomeFocused API in UIKit.
+ void assistiveTechnologySimulatedFocus();
+#endif // PLATFORM(IOS)
+
+#if PLATFORM(MAC) && !PLATFORM(IOS)
+ // Returns an ordered list of supported actions for an element.
+ JSStringRef supportedActions();
+
+ // A general description of the elements making up multiscript pre/post objects.
+ JSStringRef mathPostscriptsDescription() const;
+ JSStringRef mathPrescriptsDescription() const;
+#endif
+
+private:
+ static JSClassRef getJSClass();
+ PlatformUIElement m_element;
+
+#if PLATFORM(COCOA)
+ // A retained, platform specific object used to help manage notifications for this object.
+ NotificationHandler m_notificationHandler;
+#endif
+
+#if HAVE(ACCESSIBILITY) && (PLATFORM(GTK) || PLATFORM(EFL))
+ RefPtr<AccessibilityNotificationHandler> m_notificationHandler;
+#endif
+};
+
+#endif // AccessibilityUIElement_h
diff --git a/Tools/DumpRenderTree/CMakeLists.txt b/Tools/DumpRenderTree/CMakeLists.txt
new file mode 100644
index 000000000..5ece7a558
--- /dev/null
+++ b/Tools/DumpRenderTree/CMakeLists.txt
@@ -0,0 +1,123 @@
+set(DumpRenderTree_SOURCES
+ AccessibilityController.cpp
+ AccessibilityTextMarker.cpp
+ AccessibilityUIElement.cpp
+ CyclicRedundancyCheck.cpp
+ DumpRenderTreeCommon.cpp
+ GCController.cpp
+ JavaScriptThreading.cpp
+ PixelDumpSupport.cpp
+ TestRunner.cpp
+ WorkQueue.cpp
+)
+
+set(DumpRenderTree_LIBRARIES
+ JavaScriptCore
+ WTF
+ WebCoreTestSupport
+ WebKit
+)
+
+set(DumpRenderTree_INCLUDE_DIRECTORIES
+ ${WEBCORE_DIR}
+ ${WEBCORE_DIR}/bindings
+ ${WEBCORE_DIR}/bridge
+ ${WEBCORE_DIR}/bridge/jsc
+ ${WEBCORE_DIR}/css
+ ${WEBCORE_DIR}/dom
+ ${WEBCORE_DIR}/editing
+ ${WEBCORE_DIR}/history
+ ${WEBCORE_DIR}/html
+ ${WEBCORE_DIR}/inspector
+ ${WEBCORE_DIR}/loader
+ ${WEBCORE_DIR}/loader/cache
+ ${WEBCORE_DIR}/loader/icon
+ ${WEBCORE_DIR}/page
+ ${WEBCORE_DIR}/page/animation
+ ${WEBCORE_DIR}/platform
+ ${WEBCORE_DIR}/platform/animation
+ ${WEBCORE_DIR}/platform/graphics
+ ${WEBCORE_DIR}/platform/graphics/transforms
+ ${WEBCORE_DIR}/platform/network
+ ${WEBCORE_DIR}/platform/text
+ ${WEBCORE_DIR}/plugins
+ ${WEBCORE_DIR}/rendering
+ ${WEBCORE_DIR}/rendering/shapes
+ ${WEBCORE_DIR}/rendering/style
+ ${JAVASCRIPTCORE_DIR}
+ ${JAVASCRIPTCORE_DIR}/API
+ ${JAVASCRIPTCORE_DIR}/assembler
+ ${JAVASCRIPTCORE_DIR}/bytecode
+ ${JAVASCRIPTCORE_DIR}/dfg
+ ${JAVASCRIPTCORE_DIR}/disassembler
+ ${JAVASCRIPTCORE_DIR}/heap
+ ${JAVASCRIPTCORE_DIR}/interpreter
+ ${JAVASCRIPTCORE_DIR}/jit
+ ${JAVASCRIPTCORE_DIR}/llint
+ ${JAVASCRIPTCORE_DIR}/parser
+ ${JAVASCRIPTCORE_DIR}/profiler
+ ${JAVASCRIPTCORE_DIR}/runtime
+ ${JAVASCRIPTCORE_DIR}/ForwardingHeaders
+ ${DERIVED_SOURCES_DIR}/ForwardingHeaders
+ ${DERIVED_SOURCES_JAVASCRIPTCORE_DIR}
+ ${TOOLS_DIR}/DumpRenderTree
+ ${WTF_DIR}
+ ${CMAKE_SOURCE_DIR}/Source
+ ${CMAKE_BINARY_DIR}
+ ${DERIVED_SOURCES_DIR}
+ ${DERIVED_SOURCES_WEBCORE_DIR}
+ ${WEBCORE_DIR}/bindings/js
+ ${WEBCORE_DIR}/testing/js
+)
+
+set(TestNetscapePlugin_SOURCES
+ TestNetscapePlugin/PluginObject.cpp
+ TestNetscapePlugin/PluginTest.cpp
+ TestNetscapePlugin/TestObject.cpp
+ TestNetscapePlugin/main.cpp
+
+ TestNetscapePlugin/Tests/DocumentOpenInDestroyStream.cpp
+ TestNetscapePlugin/Tests/EvaluateJSAfterRemovingPluginElement.cpp
+ TestNetscapePlugin/Tests/FormValue.cpp
+ TestNetscapePlugin/Tests/GetURLNotifyWithURLThatFailsToLoad.cpp
+ TestNetscapePlugin/Tests/GetURLWithJavaScriptURL.cpp
+ TestNetscapePlugin/Tests/GetURLWithJavaScriptURLDestroyingPlugin.cpp
+ TestNetscapePlugin/Tests/GetUserAgentWithNullNPPFromNPPNew.cpp
+ TestNetscapePlugin/Tests/LogNPPSetWindow.cpp
+ TestNetscapePlugin/Tests/NPDeallocateCalledBeforeNPShutdown.cpp
+ TestNetscapePlugin/Tests/NPPNewFails.cpp
+ TestNetscapePlugin/Tests/NPPSetWindowCalledDuringDestruction.cpp
+ TestNetscapePlugin/Tests/NPRuntimeCallsWithNullNPP.cpp
+ TestNetscapePlugin/Tests/NPRuntimeObjectFromDestroyedPlugin.cpp
+ TestNetscapePlugin/Tests/NPRuntimeRemoveProperty.cpp
+ TestNetscapePlugin/Tests/NullNPPGetValuePointer.cpp
+ TestNetscapePlugin/Tests/PassDifferentNPPStruct.cpp
+ TestNetscapePlugin/Tests/PluginScriptableNPObjectInvokeDefault.cpp
+ TestNetscapePlugin/Tests/PluginScriptableObjectOverridesAllProperties.cpp
+ TestNetscapePlugin/Tests/PrivateBrowsing.cpp
+ TestNetscapePlugin/Tests/ToStringAndValueOfObject.cpp
+ TestNetscapePlugin/Tests/URLRedirect.cpp
+)
+
+set(TestNetscapePlugin_LIBRARIES
+ JavaScriptCore
+ WTF
+ WebCoreTestSupport
+ WebKit
+)
+
+list(APPEND TestNetscapePlugin_LIBRARIES
+ WebKit
+)
+
+WEBKIT_INCLUDE_CONFIG_FILES_IF_EXISTS()
+
+include_directories(${DumpRenderTree_INCLUDE_DIRECTORIES})
+
+add_executable(DumpRenderTree ${DumpRenderTree_SOURCES})
+target_link_libraries(DumpRenderTree ${DumpRenderTree_LIBRARIES})
+set_target_properties(DumpRenderTree PROPERTIES FOLDER "Tools")
+
+add_library(TestNetscapePlugin SHARED ${TestNetscapePlugin_SOURCES})
+target_link_libraries(TestNetscapePlugin ${TestNetscapePlugin_LIBRARIES})
+set_target_properties(TestNetscapePlugin PROPERTIES FOLDER "Tools")
diff --git a/Tools/DumpRenderTree/CyclicRedundancyCheck.cpp b/Tools/DumpRenderTree/CyclicRedundancyCheck.cpp
new file mode 100644
index 000000000..562225395
--- /dev/null
+++ b/Tools/DumpRenderTree/CyclicRedundancyCheck.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010, Robert Eisele <robert@xarg.org> All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CyclicRedundancyCheck.h"
+
+#include <wtf/Vector.h>
+
+static void makeCrcTable(unsigned crcTable[256])
+{
+ for (unsigned i = 0; i < 256; i++) {
+ unsigned c = i;
+ for (int k = 0; k < 8; k++) {
+ if (c & 1)
+ c = -306674912 ^ ((c >> 1) & 0x7fffffff);
+ else
+ c = c >> 1;
+ }
+ crcTable[i] = c;
+ }
+}
+
+unsigned computeCrc(const Vector<unsigned char>& buffer)
+{
+ static unsigned crcTable[256];
+ static bool crcTableComputed = false;
+ if (!crcTableComputed) {
+ makeCrcTable(crcTable);
+ crcTableComputed = true;
+ }
+
+ unsigned crc = 0xffffffffU;
+ for (size_t i = 0; i < buffer.size(); ++i)
+ crc = crcTable[(crc ^ buffer[i]) & 0xff] ^ ((crc >> 8) & 0x00ffffffU);
+ return crc ^ 0xffffffffU;
+}
+
diff --git a/Tools/DumpRenderTree/CyclicRedundancyCheck.h b/Tools/DumpRenderTree/CyclicRedundancyCheck.h
new file mode 100644
index 000000000..ef1d78e86
--- /dev/null
+++ b/Tools/DumpRenderTree/CyclicRedundancyCheck.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CyclicRedundancyCheck_h
+#define CyclicRedundancyCheck_h
+
+#include <wtf/Vector.h>
+
+unsigned computeCrc(const Vector<unsigned char>&);
+
+#endif
diff --git a/Tools/DumpRenderTree/DefaultPolicyDelegate.h b/Tools/DumpRenderTree/DefaultPolicyDelegate.h
new file mode 100644
index 000000000..6ff5becca
--- /dev/null
+++ b/Tools/DumpRenderTree/DefaultPolicyDelegate.h
@@ -0,0 +1,13 @@
+//
+// DefaultPolicyDelegate.h
+// DumpRenderTree
+//
+// Created by Anders Carlsson on 7/9/13.
+//
+//
+
+#import <WebKit/WebDefaultPolicyDelegate.h>
+
+@interface DefaultPolicyDelegate : WebDefaultPolicyDelegate
+
+@end
diff --git a/Tools/DumpRenderTree/DumpRenderTree.h b/Tools/DumpRenderTree/DumpRenderTree.h
new file mode 100644
index 000000000..2a2ea1467
--- /dev/null
+++ b/Tools/DumpRenderTree/DumpRenderTree.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DumpRenderTree_h
+#define DumpRenderTree_h
+
+// FIXME: Remove this when all platforms are using config.h
+#ifndef Config_H
+#include <wtf/Platform.h>
+#endif
+
+#if PLATFORM(COCOA)
+#include "DumpRenderTreeMac.h"
+#elif PLATFORM(WIN)
+#include "DumpRenderTreeWin.h"
+#elif PLATFORM(GTK)
+#include "DumpRenderTreeGtk.h"
+#elif PLATFORM(EFL)
+#include "DumpRenderTreeEfl.h"
+#endif
+
+#include <string>
+#include <wtf/RefPtr.h>
+
+#if !OS(OPENBSD)
+std::wstring urlSuitableForTestResult(const std::wstring& url);
+#endif
+
+class TestRunner;
+
+extern volatile bool done;
+
+// FIXME: This is a bad abstraction. We should insted pass this to other controller objects which need access to it.
+extern RefPtr<TestRunner> gTestRunner;
+
+void dump();
+void displayWebView();
+
+struct TestCommand {
+ TestCommand() : shouldDumpPixels(false), timeout(30000) { }
+
+ std::string pathOrURL;
+ bool shouldDumpPixels;
+ std::string expectedPixelHash;
+ int timeout; // in ms
+};
+
+TestCommand parseInputLine(const std::string&);
+
+#endif // DumpRenderTree_h
diff --git a/Tools/DumpRenderTree/DumpRenderTree.sln b/Tools/DumpRenderTree/DumpRenderTree.sln
new file mode 100644
index 000000000..7cce30330
--- /dev/null
+++ b/Tools/DumpRenderTree/DumpRenderTree.sln
@@ -0,0 +1,100 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DumpRenderTree", "win\DumpRenderTree.vcproj", "{6567DFD4-D6DE-4CD5-825D-17E353D160E1}"
+ ProjectSection(ProjectDependencies) = postProject
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C} = {C0737398-3565-439E-A2B8-AB2BE4D5430C}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestNetscapePlugin", "TestNetscapePlugin\win\TestNetscapePlugin.vcproj", "{C0737398-3565-439E-A2B8-AB2BE4D5430C}"
+ ProjectSection(ProjectDependencies) = postProject
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017} = {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageDiff", "win\ImageDiff.vcproj", "{59CC0547-70AC-499C-9B19-EC01C6F61137}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DumpRenderTreeLauncher", "win\DumpRenderTreeLauncher.vcproj", "{2974EA02-840B-4995-8719-8920A61006F1}"
+ ProjectSection(ProjectDependencies) = postProject
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1} = {6567DFD4-D6DE-4CD5-825D-17E353D160E1}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageDiffLauncher", "win\ImageDiffLauncher.vcproj", "{DD7949B6-F2B4-47C2-9C42-E21E84CB1017}"
+ ProjectSection(ProjectDependencies) = postProject
+ {59CC0547-70AC-499C-9B19-EC01C6F61137} = {59CC0547-70AC-499C-9B19-EC01C6F61137}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug_All|Win32 = Debug_All|Win32
+ Debug_Cairo_CFLite|Win32 = Debug_Cairo_CFLite|Win32
+ Debug|Win32 = Debug|Win32
+ Production|Win32 = Production|Win32
+ Release_Cairo_CFLite|Win32 = Release_Cairo_CFLite|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug_Cairo_CFLite|Win32.ActiveCfg = Debug_Cairo_CFLite|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug_Cairo_CFLite|Win32.Build.0 = Debug_Cairo_CFLite|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug|Win32.Build.0 = Debug|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Production|Win32.ActiveCfg = Production|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Production|Win32.Build.0 = Production|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Release_Cairo_CFLite|Win32.ActiveCfg = Release_Cairo_CFLite|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Release_Cairo_CFLite|Win32.Build.0 = Release_Cairo_CFLite|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Release|Win32.ActiveCfg = Release|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Release|Win32.Build.0 = Release|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug_Cairo_CFLite|Win32.ActiveCfg = Debug_Cairo_CFLite|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug_Cairo_CFLite|Win32.Build.0 = Debug_Cairo_CFLite|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug|Win32.Build.0 = Debug|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Production|Win32.ActiveCfg = Production|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Production|Win32.Build.0 = Production|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Release_Cairo_CFLite|Win32.ActiveCfg = Release_Cairo_CFLite|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Release_Cairo_CFLite|Win32.Build.0 = Release_Cairo_CFLite|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Release|Win32.ActiveCfg = Release|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Release|Win32.Build.0 = Release|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug_Cairo_CFLite|Win32.ActiveCfg = Debug_Cairo_CFLite|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug_Cairo_CFLite|Win32.Build.0 = Debug_Cairo_CFLite|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug|Win32.ActiveCfg = Debug|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug|Win32.Build.0 = Debug|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Production|Win32.ActiveCfg = Production|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Production|Win32.Build.0 = Production|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Release_Cairo_CFLite|Win32.ActiveCfg = Release_Cairo_CFLite|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Release_Cairo_CFLite|Win32.Build.0 = Release_Cairo_CFLite|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Release|Win32.ActiveCfg = Release|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Release|Win32.Build.0 = Release|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Debug_Cairo_CFLite|Win32.ActiveCfg = Debug_Cairo_CFLite|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Debug_Cairo_CFLite|Win32.Build.0 = Debug_Cairo_CFLite|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Debug|Win32.Build.0 = Debug|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Production|Win32.ActiveCfg = Production|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Production|Win32.Build.0 = Production|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Release_Cairo_CFLite|Win32.ActiveCfg = Release_Cairo_CFLite|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Release_Cairo_CFLite|Win32.Build.0 = Release_Cairo_CFLite|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Release|Win32.ActiveCfg = Release|Win32
+ {2974EA02-840B-4995-8719-8920A61006F1}.Release|Win32.Build.0 = Release|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Debug_Cairo_CFLite|Win32.ActiveCfg = Debug_Cairo_CFLite|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Debug_Cairo_CFLite|Win32.Build.0 = Debug_Cairo_CFLite|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Debug|Win32.Build.0 = Debug|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Production|Win32.ActiveCfg = Production|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Production|Win32.Build.0 = Production|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Release_Cairo_CFLite|Win32.ActiveCfg = Release_Cairo_CFLite|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Release_Cairo_CFLite|Win32.Build.0 = Release_Cairo_CFLite|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Release|Win32.ActiveCfg = Release|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Tools/DumpRenderTree/DumpRenderTreeCommon.cpp b/Tools/DumpRenderTree/DumpRenderTreeCommon.cpp
new file mode 100644
index 000000000..3cb97ebfd
--- /dev/null
+++ b/Tools/DumpRenderTree/DumpRenderTreeCommon.cpp
@@ -0,0 +1,85 @@
+#include "config.h"
+#include "DumpRenderTree.h"
+
+#include <algorithm>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+class CommandTokenizer {
+public:
+ explicit CommandTokenizer(const std::string& input)
+ : m_input(input)
+ , m_posNextSeparator(0)
+ {
+ pump();
+ }
+
+ bool hasNext() const;
+ std::string next();
+
+private:
+ void pump();
+ static const char kSeparator = '\'';
+ const std::string& m_input;
+ std::string m_next;
+ size_t m_posNextSeparator;
+};
+
+void CommandTokenizer::pump()
+{
+ if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
+ m_next = std::string();
+ return;
+ }
+ size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
+ m_posNextSeparator = m_input.find(kSeparator, start);
+ size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
+ m_next = std::string(m_input, start, size);
+}
+
+std::string CommandTokenizer::next()
+{
+ ASSERT(hasNext());
+
+ std::string oldNext = m_next;
+ pump();
+ return oldNext;
+}
+
+bool CommandTokenizer::hasNext() const
+{
+ return !m_next.empty();
+}
+
+NO_RETURN static void die(const std::string& inputLine)
+{
+ fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
+ exit(1);
+}
+
+TestCommand parseInputLine(const std::string& inputLine)
+{
+ TestCommand result;
+ CommandTokenizer tokenizer(inputLine);
+ if (!tokenizer.hasNext())
+ die(inputLine);
+
+ std::string arg = tokenizer.next();
+ result.pathOrURL = arg;
+ while (tokenizer.hasNext()) {
+ arg = tokenizer.next();
+ if (arg == std::string("--timeout")) {
+ std::string timeoutToken = tokenizer.next();
+ result.timeout = atoi(timeoutToken.c_str());
+ } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
+ result.shouldDumpPixels = true;
+ if (tokenizer.hasNext())
+ result.expectedPixelHash = tokenizer.next();
+ } else
+ die(inputLine);
+ }
+
+ return result;
+}
diff --git a/Tools/DumpRenderTree/DumpRenderTreeFileDraggingSource.h b/Tools/DumpRenderTree/DumpRenderTreeFileDraggingSource.h
new file mode 100644
index 000000000..1d42a474c
--- /dev/null
+++ b/Tools/DumpRenderTree/DumpRenderTreeFileDraggingSource.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2009, Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#if !PLATFORM(IOS)
+
+#import <Cocoa/Cocoa.h>
+
+// An implementation of NSDraggingSource for use with DumpRenderTreeDraggingInfo when dragging files
+// Used by -[EventSendingController beginDragWithFiles:]
+
+@interface DumpRenderTreeFileDraggingSource : NSObject {
+}
+
+- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag;
+
+@end
+
+#endif
diff --git a/Tools/DumpRenderTree/DumpRenderTreePrefix.h b/Tools/DumpRenderTree/DumpRenderTreePrefix.h
new file mode 100644
index 000000000..d11117ea2
--- /dev/null
+++ b/Tools/DumpRenderTree/DumpRenderTreePrefix.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2005 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <wtf/Platform.h>
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+
+#if OS(WINDOWS)
+#undef WEBCORE_EXPORT
+#define WEBCORE_EXPORT WTF_IMPORT_DECLARATION
+#endif
diff --git a/Tools/DumpRenderTree/ForwardingHeaders/runtime/ArrayBufferView.h b/Tools/DumpRenderTree/ForwardingHeaders/runtime/ArrayBufferView.h
new file mode 100644
index 000000000..7731671c8
--- /dev/null
+++ b/Tools/DumpRenderTree/ForwardingHeaders/runtime/ArrayBufferView.h
@@ -0,0 +1 @@
+#include <JavaScriptCore/ArrayBufferView.h>
diff --git a/Tools/DumpRenderTree/ForwardingHeaders/runtime/JSArrayBufferView.h b/Tools/DumpRenderTree/ForwardingHeaders/runtime/JSArrayBufferView.h
new file mode 100644
index 000000000..e38a8040f
--- /dev/null
+++ b/Tools/DumpRenderTree/ForwardingHeaders/runtime/JSArrayBufferView.h
@@ -0,0 +1 @@
+#include <JavaScriptCore/JSArrayBufferView.h>
diff --git a/Tools/DumpRenderTree/ForwardingHeaders/runtime/JSExportMacros.h b/Tools/DumpRenderTree/ForwardingHeaders/runtime/JSExportMacros.h
new file mode 100644
index 000000000..4c5dcb983
--- /dev/null
+++ b/Tools/DumpRenderTree/ForwardingHeaders/runtime/JSExportMacros.h
@@ -0,0 +1 @@
+#include <JavaScriptCore/JSExportMacros.h> \ No newline at end of file
diff --git a/Tools/DumpRenderTree/ForwardingHeaders/runtime/TypedArrayInlines.h b/Tools/DumpRenderTree/ForwardingHeaders/runtime/TypedArrayInlines.h
new file mode 100644
index 000000000..6a61945ea
--- /dev/null
+++ b/Tools/DumpRenderTree/ForwardingHeaders/runtime/TypedArrayInlines.h
@@ -0,0 +1 @@
+#include <JavaScriptCore/TypedArrayInlines.h>
diff --git a/Tools/DumpRenderTree/GCController.cpp b/Tools/DumpRenderTree/GCController.cpp
new file mode 100644
index 000000000..781a828eb
--- /dev/null
+++ b/Tools/DumpRenderTree/GCController.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "GCController.h"
+
+#include <JavaScriptCore/JSObjectRef.h>
+#include <JavaScriptCore/JSRetainPtr.h>
+
+GCController::GCController()
+{
+}
+
+GCController::~GCController()
+{
+}
+
+// Static Functions
+
+static JSValueRef collectCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ GCController* controller = static_cast<GCController*>(JSObjectGetPrivate(thisObject));
+ controller->collect();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef collectOnAlternateThreadCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ bool waitUntilDone = false;
+ if (argumentCount > 0)
+ waitUntilDone = JSValueToBoolean(context, arguments[0]);
+
+ GCController* controller = static_cast<GCController*>(JSObjectGetPrivate(thisObject));
+ controller->collectOnAlternateThread(waitUntilDone);
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef getJSObjectCountCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ GCController* controller = static_cast<GCController*>(JSObjectGetPrivate(thisObject));
+ size_t jsObjectCount = controller->getJSObjectCount();
+
+ return JSValueMakeNumber(context, jsObjectCount);
+}
+
+// Object Creation
+
+void GCController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> gcControllerStr(Adopt, JSStringCreateWithUTF8CString("GCController"));
+
+ JSClassRef classRef = getJSClass();
+ JSValueRef gcControllerObject = JSObjectMake(context, classRef, this);
+ JSClassRelease(classRef);
+
+ JSObjectSetProperty(context, windowObject, gcControllerStr.get(), gcControllerObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
+}
+
+JSClassRef GCController::getJSClass()
+{
+ static JSStaticFunction staticFunctions[] = {
+ { "collect", collectCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "collectOnAlternateThread", collectOnAlternateThreadCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "getJSObjectCount", getJSObjectCountCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { 0, 0, 0 }
+ };
+
+ static JSClassDefinition classDefinition = {
+ 0, kJSClassAttributeNone, "GCController", 0, 0, staticFunctions,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ return JSClassCreate(&classDefinition);
+}
diff --git a/Tools/DumpRenderTree/GCController.h b/Tools/DumpRenderTree/GCController.h
new file mode 100644
index 000000000..79ceafc9f
--- /dev/null
+++ b/Tools/DumpRenderTree/GCController.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCController_h
+#define GCController_h
+
+#include <JavaScriptCore/JSObjectRef.h>
+
+class GCController {
+public:
+ GCController();
+ ~GCController();
+
+ void makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception);
+
+ // Controller Methods - platfrom independant implementations
+ void collect() const;
+ void collectOnAlternateThread(bool waitUntilDone) const;
+ size_t getJSObjectCount() const;
+
+private:
+ static JSClassRef getJSClass();
+};
+
+#endif // GCController_h
diff --git a/Tools/DumpRenderTree/JavaScriptThreading.cpp b/Tools/DumpRenderTree/JavaScriptThreading.cpp
new file mode 100644
index 000000000..e2f9bade6
--- /dev/null
+++ b/Tools/DumpRenderTree/JavaScriptThreading.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * (C) 2007 Graham Dennis (graham.dennis@gmail.com)
+ * (C) 2007 Eric Seidel <eric@webkit.org>
+ * (C) 2012 Patrick Ganstere <paroga@paroga.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JavaScriptThreading.h"
+
+#include <JavaScriptCore/JavaScriptCore.h>
+#include <stdlib.h>
+#include <wtf/Assertions.h>
+#include <wtf/HashSet.h>
+#include <wtf/Lock.h>
+#include <wtf/Threading.h>
+#include <wtf/ThreadingPrimitives.h>
+#include <wtf/Vector.h>
+
+static const size_t javaScriptThreadsCount = 4;
+static bool javaScriptThreadsShouldTerminate;
+static JSContextGroupRef javaScriptThreadsGroup;
+
+static Lock& javaScriptThreadsMutex()
+{
+ DEPRECATED_DEFINE_STATIC_LOCAL(Lock, staticMutex, ());
+ return staticMutex;
+}
+
+typedef HashSet<ThreadIdentifier> ThreadSet;
+static ThreadSet& javaScriptThreads()
+{
+ DEPRECATED_DEFINE_STATIC_LOCAL(ThreadSet, staticJavaScriptThreads, ());
+ ASSERT(!javaScriptThreadsMutex().tryLock());
+ return staticJavaScriptThreads;
+}
+
+// This function exercises JSC in a loop until javaScriptThreadsShouldTerminate
+// becomes true or it probabilistically decides to spawn a replacement thread and exit.
+void runJavaScriptThread(void*)
+{
+ static const char* const script =
+ "var array = [];"
+ "for (var i = 0; i < 1024; i++) {"
+ " array.push(String(i));"
+ "}";
+
+ JSGlobalContextRef ctx;
+ {
+ LockHolder locker(javaScriptThreadsMutex());
+ ctx = JSGlobalContextCreateInGroup(javaScriptThreadsGroup, 0);
+ }
+
+ JSStringRef scriptRef;
+ {
+ LockHolder locker(javaScriptThreadsMutex());
+ scriptRef = JSStringCreateWithUTF8CString(script);
+ }
+
+ while (true) {
+ {
+ LockHolder locker(javaScriptThreadsMutex());
+ JSValueRef exception = 0;
+ JSEvaluateScript(ctx, scriptRef, 0, 0, 1, &exception);
+ ASSERT(!exception);
+ }
+
+ {
+ LockHolder locker(javaScriptThreadsMutex());
+ const size_t valuesCount = 1024;
+ JSValueRef values[valuesCount];
+ for (size_t i = 0; i < valuesCount; ++i)
+ values[i] = JSObjectMake(ctx, 0, 0);
+ }
+
+ {
+ LockHolder locker(javaScriptThreadsMutex());
+ if (javaScriptThreadsShouldTerminate)
+ break;
+ }
+
+ // Respawn probabilistically.
+ if (rand() % 5)
+ continue;
+
+ LockHolder locker(javaScriptThreadsMutex());
+ ThreadIdentifier thread = currentThread();
+ detachThread(thread);
+ javaScriptThreads().remove(thread);
+ javaScriptThreads().add(createThread(&runJavaScriptThread, 0, 0));
+ break;
+ }
+
+ LockHolder locker(javaScriptThreadsMutex());
+ JSStringRelease(scriptRef);
+ JSGarbageCollect(ctx);
+ JSGlobalContextRelease(ctx);
+}
+
+void startJavaScriptThreads()
+{
+ javaScriptThreadsGroup = JSContextGroupCreate();
+
+ LockHolder locker(javaScriptThreadsMutex());
+
+ for (size_t i = 0; i < javaScriptThreadsCount; ++i)
+ javaScriptThreads().add(createThread(&runJavaScriptThread, 0, 0));
+}
+
+void stopJavaScriptThreads()
+{
+ {
+ LockHolder locker(javaScriptThreadsMutex());
+ javaScriptThreadsShouldTerminate = true;
+ }
+
+ Vector<ThreadIdentifier, javaScriptThreadsCount> threads;
+ {
+ LockHolder locker(javaScriptThreadsMutex());
+ copyToVector(javaScriptThreads(), threads);
+ ASSERT(threads.size() == javaScriptThreadsCount);
+ }
+
+ for (size_t i = 0; i < javaScriptThreadsCount; ++i)
+ waitForThreadCompletion(threads[i]);
+
+ {
+ LockHolder locker(javaScriptThreadsMutex());
+ javaScriptThreads().clear();
+ }
+
+ JSContextGroupRelease(javaScriptThreadsGroup);
+}
diff --git a/Tools/DumpRenderTree/JavaScriptThreading.h b/Tools/DumpRenderTree/JavaScriptThreading.h
new file mode 100644
index 000000000..5bbe9cafe
--- /dev/null
+++ b/Tools/DumpRenderTree/JavaScriptThreading.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * (C) 2007 Graham Dennis (graham.dennis@gmail.com)
+ * (C) 2007 Eric Seidel <eric@webkit.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JavaScriptThreading_h
+#define JavaScriptThreading_h
+
+/* These functions start/stop threads used to abuse the JavaScript interpreter
+ and assure that our JS implementation remains threadsafe */
+
+void startJavaScriptThreads();
+void stopJavaScriptThreads();
+
+#endif // JavaScriptThreading_h
diff --git a/Tools/DumpRenderTree/PixelDumpSupport.cpp b/Tools/DumpRenderTree/PixelDumpSupport.cpp
new file mode 100644
index 000000000..aeb902ceb
--- /dev/null
+++ b/Tools/DumpRenderTree/PixelDumpSupport.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2009 Apple, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PixelDumpSupport.h"
+
+#include "CyclicRedundancyCheck.h"
+#include "DumpRenderTree.h"
+#include "TestRunner.h"
+#include <cstdio>
+#include <wtf/Assertions.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+#if USE(CG)
+#include "PixelDumpSupportCG.h"
+#elif USE(CAIRO)
+#include "PixelDumpSupportCairo.h"
+#endif
+
+void dumpWebViewAsPixelsAndCompareWithExpected(const std::string& expectedHash)
+{
+ RefPtr<BitmapContext> context;
+#if PLATFORM(MAC)
+ if (gTestRunner->isPrinting())
+ context = createPagedBitmapContext();
+ else
+#endif
+ context = createBitmapContextFromWebView(gTestRunner->testOnscreen(), gTestRunner->testRepaint(), gTestRunner->testRepaintSweepHorizontally(), gTestRunner->dumpSelectionRect());
+ ASSERT(context);
+
+ // Compute the hash of the bitmap context pixels
+ char actualHash[33];
+ computeMD5HashStringForBitmapContext(context.get(), actualHash);
+ printf("\nActualHash: %s\n", actualHash); // FIXME: No need for the leading newline.
+
+ // Check the computed hash against the expected one and dump image on mismatch
+ bool dumpImage = true;
+ if (expectedHash.length() > 0) {
+ ASSERT(expectedHash.length() == 32);
+
+ printf("\nExpectedHash: %s\n", expectedHash.c_str()); // FIXME: No need for the leading newline.
+
+ if (expectedHash == actualHash) // FIXME: do case insensitive compare
+ dumpImage = false;
+ }
+
+ if (dumpImage)
+ dumpBitmap(context.get(), actualHash);
+}
+
+static void appendIntToVector(unsigned number, Vector<unsigned char>& vector)
+{
+ size_t offset = vector.size();
+ vector.grow(offset + 4);
+ vector[offset] = ((number >> 24) & 0xff);
+ vector[offset + 1] = ((number >> 16) & 0xff);
+ vector[offset + 2] = ((number >> 8) & 0xff);
+ vector[offset + 3] = (number & 0xff);
+}
+
+static void convertChecksumToPNGComment(const char* checksum, Vector<unsigned char>& bytesToAdd)
+{
+ // Chunks of PNG files are <length>, <type>, <data>, <crc>.
+ static const char textCommentPrefix[] = "\x00\x00\x00\x29tEXtchecksum\x00";
+ static const size_t prefixLength = sizeof(textCommentPrefix) - 1; // The -1 is for the null at the end of the char[].
+ static const size_t checksumLength = 32;
+
+ bytesToAdd.append(textCommentPrefix, prefixLength);
+ bytesToAdd.append(checksum, checksumLength);
+
+ Vector<unsigned char> dataToCrc;
+ dataToCrc.append(textCommentPrefix + 4, prefixLength - 4); // Don't include the chunk length in the crc.
+ dataToCrc.append(checksum, checksumLength);
+ unsigned crc32 = computeCrc(dataToCrc);
+
+ appendIntToVector(crc32, bytesToAdd);
+}
+
+static size_t offsetAfterIHDRChunk(const unsigned char* data, const size_t dataLength)
+{
+ const int pngHeaderLength = 8;
+ const int pngIHDRChunkLength = 25; // chunk length + "IHDR" + 13 bytes of data + checksum
+ return pngHeaderLength + pngIHDRChunkLength;
+}
+
+void printPNG(const unsigned char* data, const size_t dataLength, const char* checksum)
+{
+ Vector<unsigned char> bytesToAdd;
+ convertChecksumToPNGComment(checksum, bytesToAdd);
+
+ printf("Content-Type: %s\n", "image/png");
+ printf("Content-Length: %lu\n", static_cast<unsigned long>(dataLength + bytesToAdd.size()));
+
+ size_t insertOffset = offsetAfterIHDRChunk(data, dataLength);
+
+ fwrite(data, 1, insertOffset, stdout);
+ fwrite(bytesToAdd.data(), 1, bytesToAdd.size(), stdout);
+
+ const size_t bytesToWriteInOneChunk = 1 << 15;
+ data += insertOffset;
+ size_t dataRemainingToWrite = dataLength - insertOffset;
+ while (dataRemainingToWrite) {
+ size_t bytesToWriteInThisChunk = std::min(dataRemainingToWrite, bytesToWriteInOneChunk);
+ size_t bytesWritten = fwrite(data, 1, bytesToWriteInThisChunk, stdout);
+ if (bytesWritten != bytesToWriteInThisChunk)
+ break;
+ dataRemainingToWrite -= bytesWritten;
+ data += bytesWritten;
+ }
+}
diff --git a/Tools/DumpRenderTree/PixelDumpSupport.h b/Tools/DumpRenderTree/PixelDumpSupport.h
new file mode 100644
index 000000000..a0ec1e1c7
--- /dev/null
+++ b/Tools/DumpRenderTree/PixelDumpSupport.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PixelDumpSupport_h
+#define PixelDumpSupport_h
+
+#include <string>
+
+#include <wtf/PassRefPtr.h>
+
+class BitmapContext;
+
+void computeMD5HashStringForBitmapContext(BitmapContext*, char hashString[33]);
+PassRefPtr<BitmapContext> createPagedBitmapContext();
+PassRefPtr<BitmapContext> createBitmapContextFromWebView(bool onscreen, bool incrementalRepaint, bool sweepHorizontally, bool drawSelectionRect);
+void dumpBitmap(BitmapContext*, const char* checksum);
+void dumpWebViewAsPixelsAndCompareWithExpected(const std::string& expectedHash);
+void printPNG(const unsigned char* data, const size_t dataLength, const char* checksum);
+
+#endif // PixelDumpSupport_h
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/CMakeLists.txt b/Tools/DumpRenderTree/TestNetscapePlugIn/CMakeLists.txt
new file mode 100644
index 000000000..c431667b2
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/CMakeLists.txt
@@ -0,0 +1,61 @@
+set(WEBKIT_TESTNETSCAPEPLUGIN_DIR "${TOOLS_DIR}/DumpRenderTree/TestNetscapePlugIn")
+
+set(WebKitTestNetscapePlugin_SOURCES
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/PluginObject.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/PluginTest.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/TestObject.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/main.cpp
+
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/DocumentOpenInDestroyStream.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/EvaluateJSAfterRemovingPluginElement.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/FormValue.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/GetURLNotifyWithURLThatFailsToLoad.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/GetURLWithJavaScriptURL.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/GetURLWithJavaScriptURLDestroyingPlugin.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/GetUserAgentWithNullNPPFromNPPNew.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/LogNPPSetWindow.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/NPDeallocateCalledBeforeNPShutdown.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/NPPNewFails.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/NPPSetWindowCalledDuringDestruction.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/NPRuntimeCallsWithNullNPP.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/NPRuntimeObjectFromDestroyedPlugin.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/NPRuntimeRemoveProperty.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/NullNPPGetValuePointer.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/PassDifferentNPPStruct.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/PluginScriptableNPObjectInvokeDefault.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/PluginScriptableObjectOverridesAllProperties.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/PrivateBrowsing.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/ToStringAndValueOfObject.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/URLRedirect.cpp
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/Tests/x11/CallInvalidateRectWithNullNPPArgument.cpp
+)
+
+set(WebKitTestNetscapePlugin_INCLUDE_DIRECTORIES
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}
+ ${WEBKIT_TESTNETSCAPEPLUGIN_DIR}/ForwardingHeaders
+ ${WEBCORE_DIR}
+ ${WTF_DIR}
+)
+
+set(WebKitTestNetscapePlugin_SYSTEM_INCLUDE_DIRECTORIES
+ ${X11_INCLUDE_DIR}
+)
+
+include_directories(${WebKitTestNetscapePlugin_INCLUDE_DIRECTORIES})
+include_directories(SYSTEM ${WebKitTestNetscapePlugin_SYSTEM_INCLUDE_DIRECTORIES})
+
+set(WebKitTestNetscapePlugin_LIBRARIES
+ ${X11_LIBRARIES}
+)
+
+if (WTF_OS_UNIX)
+ add_definitions(-DXP_UNIX)
+endif ()
+
+add_library(TestNetscapePlugin SHARED ${WebKitTestNetscapePlugin_SOURCES})
+target_link_libraries(TestNetscapePlugin ${WebKitTestNetscapePlugin_LIBRARIES})
+set_target_properties(TestNetscapePlugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/plugins)
+WEBKIT_SET_EXTRA_COMPILER_FLAGS(TestNetscapePlugin)
+
+# Suppress unused parameter warnings for sources in WebKit2.
+ADD_TARGET_PROPERTIES(TestNetscapePlugin COMPILE_FLAGS "-Wno-unused-parameter")
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npapi.h b/Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npapi.h
new file mode 100644
index 000000000..627bc97a9
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npapi.h
@@ -0,0 +1 @@
+#include <plugins/npapi.h>
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npfunctions.h b/Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npfunctions.h
new file mode 100644
index 000000000..54a603dbb
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npfunctions.h
@@ -0,0 +1 @@
+#include <plugins/npfunctions.h>
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npruntime.h b/Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npruntime.h
new file mode 100644
index 000000000..e435ae2ab
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/ForwardingHeaders/WebKit/npruntime.h
@@ -0,0 +1 @@
+#include <plugins/npruntime.h>
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/PluginObject.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/PluginObject.cpp
new file mode 100644
index 000000000..ae9016848
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/PluginObject.cpp
@@ -0,0 +1,1309 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Holger Hans Peter Freyther
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+
+#include "PluginTest.h"
+#include "TestObject.h"
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wtf/Platform.h>
+#include <wtf/ExportMacros.h>
+#include <wtf/Assertions.h>
+
+// Helper function which takes in the plugin window object for logging to the console object.
+static void pluginLogWithWindowObject(NPObject* windowObject, NPP instance, const char* message)
+{
+ NPVariant consoleVariant;
+ if (!browser->getproperty(instance, windowObject, browser->getstringidentifier("console"), &consoleVariant)) {
+ fprintf(stderr, "Failed to retrieve console object while logging: %s\n", message);
+ return;
+ }
+
+ NPObject* consoleObject = NPVARIANT_TO_OBJECT(consoleVariant);
+
+ NPVariant messageVariant;
+ STRINGZ_TO_NPVARIANT(message, messageVariant);
+
+ NPVariant result;
+ if (!browser->invoke(instance, consoleObject, browser->getstringidentifier("log"), &messageVariant, 1, &result)) {
+ fprintf(stderr, "Failed to invoke console.log while logging: %s\n", message);
+ browser->releaseobject(consoleObject);
+ return;
+ }
+
+ browser->releasevariantvalue(&result);
+ browser->releaseobject(consoleObject);
+}
+
+WTF_ATTRIBUTE_PRINTF(2, 0)
+void pluginLogWithArguments(NPP instance, const char* format, va_list args)
+{
+ const size_t messageBufferSize = 2048;
+ char message[messageBufferSize] = "PLUGIN: ";
+ int messageLength = sizeof("PLUGIN: ") - 1;
+ messageLength += vsnprintf(message + messageLength, messageBufferSize - 1 - messageLength, format, args);
+ message[messageLength] = '\0';
+
+ NPObject* windowObject = 0;
+ NPError error = browser->getvalue(instance, NPNVWindowNPObject, &windowObject);
+ if (error != NPERR_NO_ERROR) {
+ fprintf(stderr, "Failed to retrieve window object while logging: %s\n", message);
+ return;
+ }
+
+ pluginLogWithWindowObject(windowObject, instance, message);
+ browser->releaseobject(windowObject);
+}
+
+// Helper function to log to the console object.
+WTF_ATTRIBUTE_PRINTF(2, 3)
+void pluginLog(NPP instance, const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ pluginLogWithArguments(instance, format, args);
+ va_end(args);
+}
+
+static void pluginInvalidate(NPObject*);
+static bool pluginHasProperty(NPObject*, NPIdentifier name);
+static bool pluginHasMethod(NPObject*, NPIdentifier name);
+static bool pluginGetProperty(NPObject*, NPIdentifier name, NPVariant*);
+static bool pluginSetProperty(NPObject*, NPIdentifier name, const NPVariant*);
+static bool pluginInvoke(NPObject*, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static NPObject* pluginAllocate(NPP npp, NPClass*);
+static void pluginDeallocate(NPObject*);
+
+NPNetscapeFuncs* browser;
+NPPluginFuncs* pluginFunctions;
+
+static NPClass pluginClass = {
+ NP_CLASS_STRUCT_VERSION,
+ pluginAllocate,
+ pluginDeallocate,
+ pluginInvalidate,
+ pluginHasMethod,
+ pluginInvoke,
+ 0, // NPClass::invokeDefault,
+ pluginHasProperty,
+ pluginGetProperty,
+ pluginSetProperty,
+ 0, // NPClass::removeProperty
+ 0, // NPClass::enumerate
+ 0, // NPClass::construct
+};
+
+NPClass* getPluginClass(void)
+{
+ return &pluginClass;
+}
+
+static bool identifiersInitialized = false;
+
+enum {
+ ID_PROPERTY_PROPERTY = 0,
+ ID_PROPERTY_EVENT_LOGGING,
+ ID_PROPERTY_HAS_STREAM,
+ ID_PROPERTY_TEST_OBJECT,
+ ID_PROPERTY_LOG_DESTROY,
+ ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM,
+ ID_PROPERTY_RETURN_NEGATIVE_ONE_FROM_WRITE,
+ ID_PROPERTY_THROW_EXCEPTION_PROPERTY,
+ ID_LAST_SET_WINDOW_ARGUMENTS,
+ ID_PROPERTY_WINDOWED_PLUGIN,
+ ID_PROPERTY_TEST_OBJECT_COUNT,
+ ID_PROPERTY_DELETE_IN_GET_PROPERTY,
+ NUM_PROPERTY_IDENTIFIERS
+};
+
+static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS];
+static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
+ "property",
+ "eventLoggingEnabled",
+ "hasStream",
+ "testObject",
+ "logDestroy",
+ "returnErrorFromNewStream",
+ "returnNegativeOneFromWrite",
+ "testThrowExceptionProperty",
+ "lastSetWindowArguments",
+ "windowedPlugin",
+ "testObjectCount",
+ "deletePluginInGetProperty"
+};
+
+enum {
+ ID_TEST_CALLBACK_METHOD = 0,
+ ID_TEST_CALLBACK_METHOD_RETURN,
+ ID_TEST_GETURL,
+ ID_TEST_DOM_ACCESS,
+ ID_TEST_GET_URL_NOTIFY,
+ ID_TEST_INVOKE_DEFAULT,
+ ID_DESTROY_STREAM,
+ ID_TEST_ENUMERATE,
+ ID_TEST_GETINTIDENTIFIER,
+ ID_TEST_GET_PROPERTY,
+ ID_TEST_HAS_PROPERTY,
+ ID_TEST_HAS_METHOD,
+ ID_TEST_EVALUATE,
+ ID_TEST_GET_PROPERTY_RETURN_VALUE,
+ ID_TEST_IDENTIFIER_TO_STRING,
+ ID_TEST_IDENTIFIER_TO_INT,
+ ID_TEST_PASS_TEST_OBJECT,
+ ID_TEST_POSTURL_FILE,
+ ID_TEST_CONSTRUCT,
+ ID_TEST_THROW_EXCEPTION_METHOD,
+ ID_TEST_FAIL_METHOD,
+ ID_TEST_CLONE_OBJECT,
+ ID_TEST_SCRIPT_OBJECT_INVOKE,
+ ID_TEST_CREATE_TEST_OBJECT,
+ ID_DESTROY_NULL_STREAM,
+ ID_TEST_RELOAD_PLUGINS_NO_PAGES,
+ ID_TEST_RELOAD_PLUGINS_AND_PAGES,
+ ID_TEST_GET_BROWSER_PROPERTY,
+ ID_TEST_SET_BROWSER_PROPERTY,
+ ID_REMEMBER,
+ ID_GET_REMEMBERED_OBJECT,
+ ID_GET_AND_FORGET_REMEMBERED_OBJECT,
+ ID_REF_COUNT,
+ ID_SET_STATUS,
+ ID_RESIZE_TO,
+ ID_NORMALIZE,
+ ID_INVALIDATE_RECT,
+ ID_OBJECTS_ARE_SAME,
+ ID_TEST_DELETE_WITHIN_INVOKE,
+ NUM_METHOD_IDENTIFIERS
+};
+
+static NPIdentifier pluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
+static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
+ "testCallback",
+ "testCallbackReturn",
+ "getURL",
+ "testDOMAccess",
+ "getURLNotify",
+ "testInvokeDefault",
+ "destroyStream",
+ "testEnumerate",
+ "testGetIntIdentifier",
+ "testGetProperty",
+ "testHasProperty",
+ "testHasMethod",
+ "testEvaluate",
+ "testGetPropertyReturnValue",
+ "testIdentifierToString",
+ "testIdentifierToInt",
+ "testPassTestObject",
+ "testPostURLFile",
+ "testConstruct",
+ "testThrowException",
+ "testFail",
+ "testCloneObject",
+ "testScriptObjectInvoke",
+ "testCreateTestObject",
+ "destroyNullStream",
+ "reloadPluginsNoPages",
+ "reloadPluginsAndPages",
+ "testGetBrowserProperty",
+ "testSetBrowserProperty",
+ "remember",
+ "getRememberedObject",
+ "getAndForgetRememberedObject",
+ "refCount",
+ "setStatus",
+ "resizeTo",
+ "normalize",
+ "invalidateRect",
+ "objectsAreSame",
+ "testDeleteWithinInvoke"
+};
+
+static NPUTF8* createCStringFromNPVariant(const NPVariant* variant)
+{
+ size_t length = NPVARIANT_TO_STRING(*variant).UTF8Length;
+ NPUTF8* result = (NPUTF8*)malloc(length + 1);
+ memcpy(result, NPVARIANT_TO_STRING(*variant).UTF8Characters, length);
+ result[length] = '\0';
+ return result;
+}
+
+static void initializeIdentifiers(void)
+{
+ browser->getstringidentifiers(pluginPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, pluginPropertyIdentifiers);
+ browser->getstringidentifiers(pluginMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, pluginMethodIdentifiers);
+}
+
+static bool callDeletePlugin(NPObject* obj, NPIdentifier name, NPIdentifier identifierToMatch)
+{
+ if (name != identifierToMatch)
+ return false;
+
+ PluginObject* plugin = reinterpret_cast<PluginObject*>(obj);
+ NPObject* windowScriptObject;
+ browser->getvalue(plugin->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPIdentifier callbackIdentifier = browser->getstringidentifier("deletePlugin");
+ NPVariant browserResult;
+ if (browser->invoke(plugin->npp, windowScriptObject, callbackIdentifier, 0, 0, &browserResult))
+ browser->releasevariantvalue(&browserResult);
+ return true;
+}
+
+static bool pluginHasProperty(NPObject *obj, NPIdentifier name)
+{
+ if (callDeletePlugin(obj, name, browser->getstringidentifier("deletePluginReturnTrue")))
+ return true;
+
+ if (callDeletePlugin(obj, name, browser->getstringidentifier("deletePluginReturnFalse")))
+ return false;
+
+ for (int i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++)
+ if (name == pluginPropertyIdentifiers[i])
+ return true;
+ return false;
+}
+
+static bool pluginHasMethod(NPObject *obj, NPIdentifier name)
+{
+ if (callDeletePlugin(obj, name, browser->getstringidentifier("deletePluginInHasMethod")))
+ return true;
+
+ for (int i = 0; i < NUM_METHOD_IDENTIFIERS; i++)
+ if (name == pluginMethodIdentifiers[i])
+ return true;
+ return false;
+}
+
+static bool pluginGetProperty(NPObject* obj, NPIdentifier name, NPVariant* result)
+{
+ PluginObject* plugin = reinterpret_cast<PluginObject*>(obj);
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_PROPERTY]) {
+ static const char* originalString = "property";
+ char* buf = static_cast<char*>(browser->memalloc(strlen(originalString) + 1));
+ strcpy(buf, originalString);
+ STRINGZ_TO_NPVARIANT(buf, *result);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_EVENT_LOGGING]) {
+ BOOLEAN_TO_NPVARIANT(plugin->eventLogging, *result);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_LOG_DESTROY]) {
+ BOOLEAN_TO_NPVARIANT(plugin->logDestroy, *result);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_HAS_STREAM]) {
+ BOOLEAN_TO_NPVARIANT(plugin->stream, *result);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_TEST_OBJECT]) {
+ NPObject* testObject = plugin->testObject;
+ browser->retainobject(testObject);
+ OBJECT_TO_NPVARIANT(testObject, *result);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM]) {
+ BOOLEAN_TO_NPVARIANT(plugin->returnErrorFromNewStream, *result);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_RETURN_NEGATIVE_ONE_FROM_WRITE]) {
+ BOOLEAN_TO_NPVARIANT(plugin->returnNegativeOneFromWrite, *result);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_THROW_EXCEPTION_PROPERTY]) {
+ browser->setexception(obj, "plugin object testThrowExceptionProperty SUCCESS");
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_LAST_SET_WINDOW_ARGUMENTS]) {
+ char* buf = static_cast<char*>(browser->memalloc(256));
+ snprintf(buf, 256, "x: %d, y: %d, width: %u, height: %u, clipRect: (%u, %u, %u, %u)", (int)plugin->lastWindow.x, (int)plugin->lastWindow.y, (unsigned)plugin->lastWindow.width, (unsigned)plugin->lastWindow.height,
+ plugin->lastWindow.clipRect.left, plugin->lastWindow.clipRect.top, plugin->lastWindow.clipRect.right - plugin->lastWindow.clipRect.left, plugin->lastWindow.clipRect.bottom - plugin->lastWindow.clipRect.top);
+
+ STRINGZ_TO_NPVARIANT(buf, *result);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_TEST_OBJECT_COUNT]) {
+ INT32_TO_NPVARIANT(getTestObjectCount(), *result);
+ return true;
+ }
+
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_DELETE_IN_GET_PROPERTY]) {
+ browser->retainobject(obj);
+ callDeletePlugin(obj, name, pluginPropertyIdentifiers[ID_PROPERTY_DELETE_IN_GET_PROPERTY]);
+ NPObject* testObject = plugin->testObject;
+ browser->retainobject(testObject);
+ OBJECT_TO_NPVARIANT(testObject, *result);
+ browser->releaseobject(obj);
+ return true;
+ }
+
+ return false;
+}
+
+static bool pluginSetProperty(NPObject* obj, NPIdentifier name, const NPVariant* variant)
+{
+ PluginObject* plugin = reinterpret_cast<PluginObject*>(obj);
+ if (callDeletePlugin(obj, name, browser->getstringidentifier("deletePluginReturnTrue")))
+ return true;
+
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_EVENT_LOGGING]) {
+ plugin->eventLogging = NPVARIANT_TO_BOOLEAN(*variant);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_LOG_DESTROY]) {
+ plugin->logDestroy = NPVARIANT_TO_BOOLEAN(*variant);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM]) {
+ plugin->returnErrorFromNewStream = NPVARIANT_TO_BOOLEAN(*variant);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_RETURN_NEGATIVE_ONE_FROM_WRITE]) {
+ plugin->returnNegativeOneFromWrite = NPVARIANT_TO_BOOLEAN(*variant);
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_THROW_EXCEPTION_PROPERTY]) {
+ browser->setexception(obj, "plugin object testThrowExceptionProperty SUCCESS");
+ return true;
+ }
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_WINDOWED_PLUGIN]) {
+ browser->setvalue(plugin->npp, NPPVpluginWindowBool, (void *)NPVARIANT_TO_BOOLEAN(*variant));
+ return true;
+ }
+
+ return false;
+}
+
+static bool testDOMAccess(PluginObject* obj, const NPVariant*, uint32_t, NPVariant* result)
+{
+ // Get plug-in's DOM element
+ NPObject* elementObject;
+ if (browser->getvalue(obj->npp, NPNVPluginElementNPObject, &elementObject) == NPERR_NO_ERROR) {
+ // Get style
+ NPVariant styleVariant;
+ NPIdentifier styleIdentifier = browser->getstringidentifier("style");
+ if (browser->getproperty(obj->npp, elementObject, styleIdentifier, &styleVariant) && NPVARIANT_IS_OBJECT(styleVariant)) {
+ // Set style.border
+ NPIdentifier borderIdentifier = browser->getstringidentifier("border");
+ NPVariant borderVariant;
+ STRINGZ_TO_NPVARIANT("3px solid red", borderVariant);
+ browser->setproperty(obj->npp, NPVARIANT_TO_OBJECT(styleVariant), borderIdentifier, &borderVariant);
+ browser->releasevariantvalue(&styleVariant);
+ }
+
+ browser->releaseobject(elementObject);
+ }
+ VOID_TO_NPVARIANT(*result);
+ return true;
+}
+
+static NPIdentifier stringVariantToIdentifier(NPVariant variant)
+{
+ assert(NPVARIANT_IS_STRING(variant));
+ NPUTF8* utf8String = createCStringFromNPVariant(&variant);
+ NPIdentifier identifier = browser->getstringidentifier(utf8String);
+ free(utf8String);
+ return identifier;
+}
+
+static NPIdentifier int32VariantToIdentifier(NPVariant variant)
+{
+ assert(NPVARIANT_IS_INT32(variant));
+ int32_t integer = NPVARIANT_TO_INT32(variant);
+ return browser->getintidentifier(integer);
+}
+
+static NPIdentifier doubleVariantToIdentifier(NPVariant variant)
+{
+ assert(NPVARIANT_IS_DOUBLE(variant));
+ double value = NPVARIANT_TO_DOUBLE(variant);
+ // Sadly there is no "getdoubleidentifier"
+ int32_t integer = static_cast<int32_t>(value);
+ return browser->getintidentifier(integer);
+}
+
+static NPIdentifier variantToIdentifier(NPVariant variant)
+{
+ if (NPVARIANT_IS_STRING(variant))
+ return stringVariantToIdentifier(variant);
+ if (NPVARIANT_IS_INT32(variant))
+ return int32VariantToIdentifier(variant);
+ if (NPVARIANT_IS_DOUBLE(variant))
+ return doubleVariantToIdentifier(variant);
+ return 0;
+}
+
+static bool testIdentifierToString(PluginObject*, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 1)
+ return true;
+ NPIdentifier identifier = variantToIdentifier(args[0]);
+ if (!identifier)
+ return true;
+ NPUTF8* utf8String = browser->utf8fromidentifier(identifier);
+ if (!utf8String)
+ return true;
+ STRINGZ_TO_NPVARIANT(utf8String, *result);
+ return true;
+}
+
+static bool testIdentifierToInt(PluginObject*, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 1)
+ return false;
+ NPIdentifier identifier = variantToIdentifier(args[0]);
+ if (!identifier)
+ return false;
+ int32_t integer = browser->intfromidentifier(identifier);
+ INT32_TO_NPVARIANT(integer, *result);
+ return true;
+}
+
+static bool testPassTestObject(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 2 || !NPVARIANT_IS_STRING(args[0]))
+ return false;
+
+ NPObject* windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
+ NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
+ free(callbackString);
+
+ NPVariant browserResult;
+ browser->invoke(obj->npp, windowScriptObject, callbackIdentifier, &args[1], 1, &browserResult);
+ browser->releasevariantvalue(&browserResult);
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+}
+
+static bool testCallback(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (!argCount || !NPVARIANT_IS_STRING(args[0]))
+ return false;
+
+ NPObject* windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
+ NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
+ free(callbackString);
+
+ NPVariant browserResult;
+ if (browser->invoke(obj->npp, windowScriptObject, callbackIdentifier, 0, 0, &browserResult))
+ browser->releasevariantvalue(&browserResult);
+
+ browser->releaseobject(windowScriptObject);
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+}
+
+static bool testCallbackReturn(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
+ return false;
+
+ NPObject* windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
+ NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
+ free(callbackString);
+
+ NPVariant callbackArgs[1];
+ OBJECT_TO_NPVARIANT(windowScriptObject, callbackArgs[0]);
+
+ NPVariant browserResult;
+ browser->invoke(obj->npp, windowScriptObject, callbackIdentifier,
+ callbackArgs, 1, &browserResult);
+
+ if (NPVARIANT_IS_OBJECT(browserResult))
+ OBJECT_TO_NPVARIANT(NPVARIANT_TO_OBJECT(browserResult), *result);
+ else {
+ browser->releasevariantvalue(&browserResult);
+ VOID_TO_NPVARIANT(*result);
+ }
+
+ return true;
+}
+
+static bool getURL(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount == 2 && NPVARIANT_IS_STRING(args[0]) && NPVARIANT_IS_STRING(args[1])) {
+ NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
+ NPUTF8* targetString = createCStringFromNPVariant(&args[1]);
+ NPError npErr = browser->geturl(obj->npp, urlString, targetString);
+ free(urlString);
+ free(targetString);
+
+ INT32_TO_NPVARIANT(npErr, *result);
+ return true;
+ }
+ if (argCount == 1 && NPVARIANT_IS_STRING(args[0])) {
+ NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
+ NPError npErr = browser->geturl(obj->npp, urlString, 0);
+ free(urlString);
+
+ INT32_TO_NPVARIANT(npErr, *result);
+ return true;
+ }
+ return false;
+}
+
+static bool getURLNotify(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 3 || !NPVARIANT_IS_STRING(args[0])
+ || (!NPVARIANT_IS_STRING(args[1]) && !NPVARIANT_IS_NULL(args[1]))
+ || !NPVARIANT_IS_STRING(args[2]))
+ return false;
+
+ NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
+ NPUTF8* targetString = (NPVARIANT_IS_STRING(args[1]) ? createCStringFromNPVariant(&args[1]) : 0);
+ NPUTF8* callbackString = createCStringFromNPVariant(&args[2]);
+
+ NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
+ browser->geturlnotify(obj->npp, urlString, targetString, callbackIdentifier);
+
+ free(urlString);
+ free(targetString);
+ free(callbackString);
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+}
+
+static bool testInvokeDefault(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (!NPVARIANT_IS_OBJECT(args[0]))
+ return false;
+
+ NPObject* callback = NPVARIANT_TO_OBJECT(args[0]);
+
+ NPVariant invokeArgs[1];
+ NPVariant browserResult;
+
+ STRINGZ_TO_NPVARIANT("test", invokeArgs[0]);
+ bool retval = browser->invokeDefault(obj->npp, callback, invokeArgs, 1, &browserResult);
+
+ if (retval)
+ browser->releasevariantvalue(&browserResult);
+
+ BOOLEAN_TO_NPVARIANT(retval, *result);
+ return true;
+}
+
+static bool destroyStream(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ NPError npError = browser->destroystream(obj->npp, obj->stream, NPRES_USER_BREAK);
+ INT32_TO_NPVARIANT(npError, *result);
+ return true;
+}
+
+static bool destroyNullStream(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ NPError npError = browser->destroystream(obj->npp, 0, NPRES_USER_BREAK);
+ INT32_TO_NPVARIANT(npError, *result);
+ return true;
+}
+
+static bool testEnumerate(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) || !NPVARIANT_IS_OBJECT(args[1]))
+ return false;
+
+ uint32_t count;
+ NPIdentifier* identifiers;
+ if (browser->enumerate(obj->npp, NPVARIANT_TO_OBJECT(args[0]), &identifiers, &count)) {
+ NPObject* outArray = NPVARIANT_TO_OBJECT(args[1]);
+ NPIdentifier pushIdentifier = browser->getstringidentifier("push");
+
+ for (uint32_t i = 0; i < count; i++) {
+ NPUTF8* string = browser->utf8fromidentifier(identifiers[i]);
+
+ if (!string)
+ continue;
+
+ NPVariant args[1];
+ STRINGZ_TO_NPVARIANT(string, args[0]);
+ NPVariant browserResult;
+ if (browser->invoke(obj->npp, outArray, pushIdentifier, args, 1, &browserResult))
+ browser->releasevariantvalue(&browserResult);
+ browser->memfree(string);
+ }
+
+ browser->memfree(identifiers);
+ }
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+}
+
+static bool testGetIntIdentifier(PluginObject*, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 1 || !NPVARIANT_IS_DOUBLE(args[0]))
+ return false;
+
+ NPIdentifier identifier = browser->getintidentifier((int)NPVARIANT_TO_DOUBLE(args[0]));
+ INT32_TO_NPVARIANT((int32_t)(long long)identifier, *result);
+ return true;
+}
+
+static bool testGetProperty(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (!argCount)
+ return false;
+
+ NPObject* object;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &object);
+
+ for (uint32_t i = 0; i < argCount; i++) {
+ assert(NPVARIANT_IS_STRING(args[i]));
+ NPUTF8* propertyString = createCStringFromNPVariant(&args[i]);
+ NPIdentifier propertyIdentifier = browser->getstringidentifier(propertyString);
+ free(propertyString);
+
+ NPVariant variant;
+ bool retval = browser->getproperty(obj->npp, object, propertyIdentifier, &variant);
+ browser->releaseobject(object);
+
+ if (!retval)
+ break;
+
+ if (i + 1 < argCount) {
+ assert(NPVARIANT_IS_OBJECT(variant));
+ object = NPVARIANT_TO_OBJECT(variant);
+ } else {
+ *result = variant;
+ return true;
+ }
+ }
+
+ VOID_TO_NPVARIANT(*result);
+ return false;
+}
+
+static bool testHasProperty(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) || !NPVARIANT_IS_STRING(args[1]))
+ return false;
+
+ NPUTF8* propertyString = createCStringFromNPVariant(&args[1]);
+ NPIdentifier propertyIdentifier = browser->getstringidentifier(propertyString);
+ free(propertyString);
+
+ bool retval = browser->hasproperty(obj->npp, NPVARIANT_TO_OBJECT(args[0]), propertyIdentifier);
+
+ BOOLEAN_TO_NPVARIANT(retval, *result);
+ return true;
+}
+
+static bool testHasMethod(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) || !NPVARIANT_IS_STRING(args[1]))
+ return false;
+
+ NPUTF8* propertyString = createCStringFromNPVariant(&args[1]);
+ NPIdentifier propertyIdentifier = browser->getstringidentifier(propertyString);
+ free(propertyString);
+
+ bool retval = browser->hasmethod(obj->npp, NPVARIANT_TO_OBJECT(args[0]), propertyIdentifier);
+
+ BOOLEAN_TO_NPVARIANT(retval, *result);
+ return true;
+}
+
+static bool testEvaluate(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
+ return false;
+ NPObject* windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPString s = NPVARIANT_TO_STRING(args[0]);
+
+ bool retval = browser->evaluate(obj->npp, windowScriptObject, &s, result);
+ browser->releaseobject(windowScriptObject);
+ return retval;
+}
+
+static bool testGetPropertyReturnValue(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) || !NPVARIANT_IS_STRING(args[1]))
+ return false;
+
+ NPUTF8* propertyString = createCStringFromNPVariant(&args[1]);
+ NPIdentifier propertyIdentifier = browser->getstringidentifier(propertyString);
+ free(propertyString);
+
+ NPVariant variant;
+ bool retval = browser->getproperty(obj->npp, NPVARIANT_TO_OBJECT(args[0]), propertyIdentifier, &variant);
+ if (retval)
+ browser->releasevariantvalue(&variant);
+
+ BOOLEAN_TO_NPVARIANT(retval, *result);
+ return true;
+}
+
+static char* toCString(const NPString& string)
+{
+ char* result = static_cast<char*>(malloc(string.UTF8Length + 1));
+ memcpy(result, string.UTF8Characters, string.UTF8Length);
+ result[string.UTF8Length] = '\0';
+
+ return result;
+}
+
+static bool testPostURLFile(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 4 || !NPVARIANT_IS_STRING(args[0]) || !NPVARIANT_IS_STRING(args[1]) || !NPVARIANT_IS_STRING(args[2]) || !NPVARIANT_IS_STRING(args[3]))
+ return false;
+
+ NPString urlString = NPVARIANT_TO_STRING(args[0]);
+ char* url = toCString(urlString);
+
+ NPString targetString = NPVARIANT_TO_STRING(args[1]);
+ char* target = toCString(targetString);
+
+ NPString pathString = NPVARIANT_TO_STRING(args[2]);
+ char* path = toCString(pathString);
+
+ NPString contentsString = NPVARIANT_TO_STRING(args[3]);
+
+ FILE* tempFile = fopen(path, "w");
+ if (!tempFile)
+ return false;
+
+ if (!fwrite(contentsString.UTF8Characters, contentsString.UTF8Length, 1, tempFile))
+ return false;
+
+ fclose(tempFile);
+
+ NPError error = browser->posturl(obj->npp, url, target, pathString.UTF8Length, path, TRUE);
+
+ free(path);
+ free(target);
+ free(url);
+
+ BOOLEAN_TO_NPVARIANT(error == NPERR_NO_ERROR, *result);
+ return true;
+}
+
+static bool testConstruct(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (!argCount || !NPVARIANT_IS_OBJECT(args[0]))
+ return false;
+
+ return browser->construct(obj->npp, NPVARIANT_TO_OBJECT(args[0]), args + 1, argCount - 1, result);
+}
+
+// Invoke a script callback to get a script NPObject. Then call a method on the
+// script NPObject passing it a freshly created NPObject.
+static bool testScriptObjectInvoke(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 2 || !NPVARIANT_IS_STRING(args[0]) || !NPVARIANT_IS_STRING(args[1]))
+ return false;
+ NPObject* windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ // Arg1 is the name of the callback
+ NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
+ NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
+ free(callbackString);
+
+ // Invoke a callback that returns a script object
+ NPVariant object_result;
+ browser->invoke(obj->npp, windowScriptObject, callbackIdentifier, &args[1], 1, &object_result);
+
+ // Script object returned
+ NPObject* script_object = object_result.value.objectValue;
+
+ // Arg2 is the name of the method to be called on the script object
+ NPUTF8* object_mehod_string = createCStringFromNPVariant(&args[1]);
+ NPIdentifier object_method = browser->getstringidentifier(object_mehod_string);
+ free(object_mehod_string);
+
+ // Create a fresh NPObject to be passed as an argument
+ NPObject* object_arg = browser->createobject(obj->npp, &pluginClass);
+ NPVariant invoke_args[1];
+ OBJECT_TO_NPVARIANT(object_arg, invoke_args[0]);
+
+ // Invoke the script method
+ NPVariant object_method_result;
+ browser->invoke(obj->npp, script_object, object_method, invoke_args, 1, &object_method_result);
+
+ browser->releasevariantvalue(&object_result);
+ VOID_TO_NPVARIANT(*result);
+ if (NPVARIANT_IS_OBJECT(object_method_result)) {
+ // Now return the callbacks return value back to our caller.
+ // BUG 897451: This should be the same as the
+ // windowScriptObject, but its not (in Chrome) - or at least, it
+ // has a different refcount. This means Chrome will delete the
+ // object before returning it and the calling JS gets a garbage
+ // value. Firefox handles it fine.
+ OBJECT_TO_NPVARIANT(NPVARIANT_TO_OBJECT(object_method_result), *result);
+ } else {
+ browser->releasevariantvalue(&object_method_result);
+ VOID_TO_NPVARIANT(*result);
+ }
+
+ browser->releaseobject(object_arg);
+
+ return true;
+}
+
+// Helper function to notify the layout test controller that the test completed.
+void notifyTestCompletion(NPP npp, NPObject* object)
+{
+ NPVariant result;
+ NPString script;
+ script.UTF8Characters = "javascript:window.testRunner.notifyDone();";
+ script.UTF8Length = strlen("javascript:window.testRunner.notifyDone();");
+ browser->evaluate(npp, object, &script, &result);
+ browser->releasevariantvalue(&result);
+}
+
+bool testDocumentOpen(NPP npp)
+{
+ NPIdentifier documentId = browser->getstringidentifier("document");
+ NPIdentifier openId = browser->getstringidentifier("open");
+
+ NPObject* windowObject = 0;
+ browser->getvalue(npp, NPNVWindowNPObject, &windowObject);
+ if (!windowObject)
+ return false;
+
+ NPVariant docVariant;
+ browser->getproperty(npp, windowObject, documentId, &docVariant);
+ if (docVariant.type != NPVariantType_Object) {
+ browser->releaseobject(windowObject);
+ return false;
+ }
+
+ NPObject* documentObject = NPVARIANT_TO_OBJECT(docVariant);
+
+ NPVariant openArgs[2];
+ STRINGZ_TO_NPVARIANT("text/html", openArgs[0]);
+ STRINGZ_TO_NPVARIANT("_blank", openArgs[1]);
+
+ NPVariant result;
+ if (!browser->invoke(npp, documentObject, openId, openArgs, 2, &result)) {
+ browser->releaseobject(windowObject);
+ browser->releaseobject(documentObject);
+ return false;
+ }
+
+ browser->releaseobject(documentObject);
+
+ if (result.type != NPVariantType_Object) {
+ browser->releaseobject(windowObject);
+ browser->releasevariantvalue(&result);
+ return false;
+ }
+
+ pluginLogWithWindowObject(windowObject, npp, "PLUGIN: DOCUMENT OPEN SUCCESS");
+ notifyTestCompletion(npp, result.value.objectValue);
+ browser->releaseobject(result.value.objectValue);
+ browser->releaseobject(windowObject);
+ return true;
+}
+
+bool testWindowOpen(NPP npp)
+{
+ NPIdentifier openId = browser->getstringidentifier("open");
+
+ NPObject* windowObject = 0;
+ browser->getvalue(npp, NPNVWindowNPObject, &windowObject);
+ if (!windowObject)
+ return false;
+
+ NPVariant openArgs[2];
+ STRINGZ_TO_NPVARIANT("about:blank", openArgs[0]);
+ STRINGZ_TO_NPVARIANT("_blank", openArgs[1]);
+
+ NPVariant result;
+ if (!browser->invoke(npp, windowObject, openId, openArgs, 2, &result)) {
+ browser->releaseobject(windowObject);
+ return false;
+ }
+
+ if (result.type != NPVariantType_Object) {
+ browser->releaseobject(windowObject);
+ browser->releasevariantvalue(&result);
+ return false;
+ }
+
+ pluginLogWithWindowObject(windowObject, npp, "PLUGIN: WINDOW OPEN SUCCESS");
+ notifyTestCompletion(npp, result.value.objectValue);
+ browser->releaseobject(result.value.objectValue);
+ browser->releaseobject(windowObject);
+ return true;
+}
+
+static bool testSetStatus(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ char* message = 0;
+ if (argCount && NPVARIANT_IS_STRING(args[0])) {
+ NPString statusString = NPVARIANT_TO_STRING(args[0]);
+ message = toCString(statusString);
+ }
+
+ browser->status(obj->npp, message);
+
+ free(message);
+ return true;
+}
+
+static bool testResizeTo(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ VOID_TO_NPVARIANT(*result);
+
+ NPObject* windowObject;
+ if (NPERR_NO_ERROR != browser->getvalue(obj->npp, NPNVWindowNPObject, &windowObject))
+ return false;
+
+ NPVariant callResult;
+ if (browser->invoke(obj->npp, windowObject, browser->getstringidentifier("resizePlugin"), args, argCount, &callResult))
+ browser->releasevariantvalue(&callResult);
+
+ // Force layout.
+ if (browser->getproperty(obj->npp, windowObject, browser->getstringidentifier("pageYOffset"), &callResult))
+ browser->releasevariantvalue(&callResult);
+
+ return true;
+}
+
+static bool normalizeOverride(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ VOID_TO_NPVARIANT(*result);
+
+ NPObject* windowObject;
+ if (NPERR_NO_ERROR != browser->getvalue(obj->npp, NPNVWindowNPObject, &windowObject))
+ return false;
+
+ NPVariant callResult;
+ if (browser->invoke(obj->npp, windowObject, browser->getstringidentifier("pluginCallback"), args, argCount, &callResult))
+ browser->releasevariantvalue(&callResult);
+
+ return true;
+}
+
+static bool invalidateRect(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 4)
+ return false;
+
+ NPRect rect;
+ rect.left = static_cast<int>(NPVARIANT_TO_DOUBLE(args[0]));
+ rect.top = static_cast<int>(NPVARIANT_TO_DOUBLE(args[1]));
+ rect.right = static_cast<int>(NPVARIANT_TO_DOUBLE(args[2]));
+ rect.bottom = static_cast<int>(NPVARIANT_TO_DOUBLE(args[3]));
+
+ browser->invalidaterect(obj->npp, &rect);
+ return true;
+}
+
+static bool objectsAreSame(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) || !NPVARIANT_IS_OBJECT(args[1]))
+ return false;
+
+ BOOLEAN_TO_NPVARIANT(NPVARIANT_TO_OBJECT(args[0]) == NPVARIANT_TO_OBJECT(args[1]), *result);
+ return true;
+}
+
+static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
+ if (name == pluginMethodIdentifiers[ID_TEST_CALLBACK_METHOD])
+ return testCallback(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_CALLBACK_METHOD_RETURN])
+ return testCallbackReturn(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_GETURL])
+ return getURL(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_DOM_ACCESS])
+ return testDOMAccess(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_GET_URL_NOTIFY])
+ return getURLNotify(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_INVOKE_DEFAULT])
+ return testInvokeDefault(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_ENUMERATE])
+ return testEnumerate(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_DESTROY_STREAM])
+ return destroyStream(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_GETINTIDENTIFIER])
+ return testGetIntIdentifier(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_EVALUATE])
+ return testEvaluate(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_GET_PROPERTY])
+ return testGetProperty(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_GET_PROPERTY_RETURN_VALUE])
+ return testGetPropertyReturnValue(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_HAS_PROPERTY])
+ return testHasProperty(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_HAS_METHOD])
+ return testHasMethod(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_IDENTIFIER_TO_STRING])
+ return testIdentifierToString(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_IDENTIFIER_TO_INT])
+ return testIdentifierToInt(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_PASS_TEST_OBJECT])
+ return testPassTestObject(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_POSTURL_FILE])
+ return testPostURLFile(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_CONSTRUCT])
+ return testConstruct(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_SCRIPT_OBJECT_INVOKE])
+ return testScriptObjectInvoke(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_THROW_EXCEPTION_METHOD]) {
+ browser->setexception(header, "plugin object testThrowException SUCCESS");
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_TEST_FAIL_METHOD]) {
+ NPObject* windowScriptObject;
+ browser->getvalue(plugin->npp, NPNVWindowNPObject, &windowScriptObject);
+ browser->invoke(plugin->npp, windowScriptObject, name, args, argCount, result);
+ return false;
+ }
+ if (name == pluginMethodIdentifiers[ID_TEST_CLONE_OBJECT]) {
+ NPObject* new_object = browser->createobject(plugin->npp, &pluginClass);
+ assert(new_object->referenceCount == 1);
+ OBJECT_TO_NPVARIANT(new_object, *result);
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_TEST_CREATE_TEST_OBJECT]) {
+ NPObject* testObject = browser->createobject(plugin->npp, getTestClass());
+ assert(testObject->referenceCount == 1);
+ OBJECT_TO_NPVARIANT(testObject, *result);
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_DESTROY_NULL_STREAM])
+ return destroyNullStream(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_RELOAD_PLUGINS_NO_PAGES]) {
+ browser->reloadplugins(false);
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_TEST_RELOAD_PLUGINS_AND_PAGES]) {
+ browser->reloadplugins(true);
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_TEST_GET_BROWSER_PROPERTY]) {
+ browser->getproperty(plugin->npp, NPVARIANT_TO_OBJECT(args[0]), stringVariantToIdentifier(args[1]), result);
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_TEST_SET_BROWSER_PROPERTY]) {
+ browser->setproperty(plugin->npp, NPVARIANT_TO_OBJECT(args[0]), stringVariantToIdentifier(args[1]), &args[2]);
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_REMEMBER]) {
+ if (plugin->rememberedObject)
+ browser->releaseobject(plugin->rememberedObject);
+ plugin->rememberedObject = NPVARIANT_TO_OBJECT(args[0]);
+ browser->retainobject(plugin->rememberedObject);
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_GET_REMEMBERED_OBJECT]) {
+ assert(plugin->rememberedObject);
+ browser->retainobject(plugin->rememberedObject);
+ OBJECT_TO_NPVARIANT(plugin->rememberedObject, *result);
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_GET_AND_FORGET_REMEMBERED_OBJECT]) {
+ assert(plugin->rememberedObject);
+ OBJECT_TO_NPVARIANT(plugin->rememberedObject, *result);
+ plugin->rememberedObject = 0;
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_REF_COUNT]) {
+ uint32_t refCount = NPVARIANT_TO_OBJECT(args[0])->referenceCount;
+ INT32_TO_NPVARIANT(refCount, *result);
+ return true;
+ }
+ if (name == pluginMethodIdentifiers[ID_SET_STATUS])
+ return testSetStatus(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_RESIZE_TO])
+ return testResizeTo(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_NORMALIZE])
+ return normalizeOverride(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_INVALIDATE_RECT])
+ return invalidateRect(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_OBJECTS_ARE_SAME])
+ return objectsAreSame(plugin, args, argCount, result);
+ if (name == pluginMethodIdentifiers[ID_TEST_DELETE_WITHIN_INVOKE]) {
+ NPObject* newObject = browser->createobject(plugin->npp, &pluginClass);
+ OBJECT_TO_NPVARIANT(newObject, *result);
+ callDeletePlugin(header, name, pluginMethodIdentifiers[ID_TEST_DELETE_WITHIN_INVOKE]);
+ return true;
+ }
+ return false;
+}
+
+static void pluginInvalidate(NPObject* header)
+{
+ PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
+ plugin->testObject = 0;
+ plugin->rememberedObject = 0;
+}
+
+static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
+{
+ PluginObject* newInstance = (PluginObject*)malloc(sizeof(PluginObject));
+
+ if (!identifiersInitialized) {
+ identifiersInitialized = true;
+ initializeIdentifiers();
+ }
+
+ newInstance->pluginTest = 0;
+ newInstance->npp = npp;
+ newInstance->testObject = browser->createobject(npp, getTestClass());
+ newInstance->rememberedObject = 0;
+ newInstance->eventLogging = FALSE;
+ newInstance->onStreamLoad = 0;
+ newInstance->onStreamDestroy = 0;
+ newInstance->onDestroy = 0;
+ newInstance->onURLNotify = 0;
+ newInstance->onSetWindow = 0;
+ newInstance->onPaintEvent = 0;
+ newInstance->logDestroy = FALSE;
+ newInstance->logSetWindow = FALSE;
+ newInstance->returnErrorFromNewStream = FALSE;
+ newInstance->returnNegativeOneFromWrite = FALSE;
+ newInstance->stream = 0;
+
+ newInstance->firstUrl = 0;
+ newInstance->firstHeaders = 0;
+ newInstance->lastUrl = 0;
+ newInstance->lastHeaders = 0;
+
+ newInstance->testGetURLOnDestroy = FALSE;
+ newInstance->testWindowOpen = FALSE;
+ newInstance->testKeyboardFocusForPlugins = FALSE;
+
+ newInstance->mouseDownForEvaluateScript = FALSE;
+ newInstance->evaluateScriptOnMouseDownOrKeyDown = 0;
+
+ return (NPObject*)newInstance;
+}
+
+static void pluginDeallocate(NPObject* header)
+{
+ PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
+ delete plugin->pluginTest;
+ if (plugin->testObject)
+ browser->releaseobject(plugin->testObject);
+ if (plugin->rememberedObject)
+ browser->releaseobject(plugin->rememberedObject);
+
+ free(plugin->firstUrl);
+ free(plugin->firstHeaders);
+ free(plugin->lastUrl);
+ free(plugin->lastHeaders);
+ free(plugin);
+}
+
+void handleCallback(PluginObject* object, const char *url, NPReason reason, void *notifyData)
+{
+ assert(object);
+
+ NPVariant args[2];
+
+ NPObject* windowScriptObject;
+ browser->getvalue(object->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPIdentifier callbackIdentifier = notifyData;
+
+ INT32_TO_NPVARIANT(reason, args[0]);
+
+ char* strHdr = 0;
+ if (object->firstUrl && object->firstHeaders && object->lastUrl && object->lastHeaders) {
+ // Format expected by JavaScript validator: four fields separated by \n\n:
+ // First URL; first header block; last URL; last header block.
+ // Note that header blocks already end with \n due to how NPStream::headers works.
+ int len = strlen(object->firstUrl) + 2
+ + strlen(object->firstHeaders) + 1
+ + strlen(object->lastUrl) + 2
+ + strlen(object->lastHeaders) + 1;
+ strHdr = (char*)malloc(len + 1);
+ snprintf(strHdr, len + 1, "%s\n\n%s\n%s\n\n%s\n",
+ object->firstUrl, object->firstHeaders, object->lastUrl, object->lastHeaders);
+ STRINGN_TO_NPVARIANT(strHdr, len, args[1]);
+ } else
+ NULL_TO_NPVARIANT(args[1]);
+
+ NPVariant browserResult;
+ if (browser->invoke(object->npp, windowScriptObject, callbackIdentifier, args, 2, &browserResult))
+ browser->releasevariantvalue(&browserResult);
+
+ free(strHdr);
+}
+
+void notifyStream(PluginObject* object, const char *url, const char *headers)
+{
+ if (!object->firstUrl) {
+ if (url)
+ object->firstUrl = strdup(url);
+ if (headers)
+ object->firstHeaders = strdup(headers);
+ } else {
+ free(object->lastUrl);
+ free(object->lastHeaders);
+ object->lastUrl = (url ? strdup(url) : 0);
+ object->lastHeaders = (headers ? strdup(headers) : 0);
+ }
+}
+
+void testNPRuntime(NPP npp)
+{
+ NPObject* windowScriptObject;
+ browser->getvalue(npp, NPNVWindowNPObject, &windowScriptObject);
+
+ // Invoke
+ NPIdentifier testNPInvoke = browser->getstringidentifier("testNPInvoke");
+ NPVariant args[7];
+
+ VOID_TO_NPVARIANT(args[0]);
+ NULL_TO_NPVARIANT(args[1]);
+ BOOLEAN_TO_NPVARIANT(true, args[2]);
+ INT32_TO_NPVARIANT(242, args[3]);
+ DOUBLE_TO_NPVARIANT(242.242, args[4]);
+ STRINGZ_TO_NPVARIANT("Hello, World", args[5]);
+ OBJECT_TO_NPVARIANT(windowScriptObject, args[6]);
+
+ NPVariant result;
+ if (browser->invoke(npp, windowScriptObject, testNPInvoke, args, 7, &result))
+ browser->releasevariantvalue(&result);
+
+ browser->releaseobject(windowScriptObject);
+}
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/PluginObject.h b/Tools/DumpRenderTree/TestNetscapePlugIn/PluginObject.h
new file mode 100644
index 000000000..6b9bd07a4
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/PluginObject.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PluginObject_h
+#define PluginObject_h
+
+#include <WebKit/npfunctions.h>
+#include <stdarg.h>
+
+class PluginTest;
+
+extern NPNetscapeFuncs *browser;
+extern NPPluginFuncs* pluginFunctions;
+
+typedef struct {
+ NPObject header;
+
+ PluginTest* pluginTest;
+
+ NPP npp;
+ NPBool eventLogging;
+ NPBool logSetWindow;
+ NPBool logDestroy;
+ NPBool returnNegativeOneFromWrite;
+ NPBool returnErrorFromNewStream;
+ NPObject* testObject;
+ NPObject* rememberedObject;
+ NPStream* stream;
+ NPBool testGetURLOnDestroy;
+ NPBool testWindowOpen;
+ NPBool testKeyboardFocusForPlugins;
+ NPBool mouseDownForEvaluateScript;
+ char* onStreamLoad;
+ char* onStreamDestroy;
+ char* onDestroy;
+ char* onURLNotify;
+ char* onSetWindow;
+ char* onPaintEvent;
+ char* firstUrl;
+ char* firstHeaders;
+ char* lastUrl;
+ char* lastHeaders;
+ char* evaluateScriptOnMouseDownOrKeyDown;
+#ifdef XP_MACOSX
+ NPEventModel eventModel;
+#endif
+#ifdef XP_MACOSX
+ void* coreAnimationLayer;
+#endif
+ NPWindow lastWindow;
+} PluginObject;
+
+extern NPClass *getPluginClass(void);
+extern void handleCallback(PluginObject* object, const char *url, NPReason reason, void *notifyData);
+extern void notifyStream(PluginObject* object, const char *url, const char *headers);
+extern void testNPRuntime(NPP npp);
+extern void pluginLog(NPP instance, const char* format, ...);
+extern void pluginLogWithArguments(NPP instance, const char* format, va_list args);
+extern bool testDocumentOpen(NPP npp);
+extern bool testWindowOpen(NPP npp);
+
+#ifdef XP_MACOSX
+extern void* createCoreAnimationLayer();
+#endif
+
+#endif
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/PluginTest.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/PluginTest.cpp
new file mode 100644
index 000000000..da78148d4
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/PluginTest.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include "PluginObject.h"
+#include <assert.h>
+#include <string.h>
+#include <wtf/Platform.h>
+#include <wtf/ExportMacros.h>
+#include <wtf/Assertions.h>
+
+#if defined(XP_UNIX) || defined(ANDROID)
+#include <unistd.h>
+#endif
+
+using namespace std;
+extern NPNetscapeFuncs *browser;
+
+static void (*shutdownFunction)();
+
+PluginTest* PluginTest::create(NPP npp, const string& identifier)
+{
+ if (identifier.empty())
+ return new PluginTest(npp, identifier);
+
+ CreateTestFunction createTestFunction = createTestFunctions()[identifier];
+ if (createTestFunction)
+ return createTestFunction(npp, identifier);
+
+ return 0;
+}
+
+PluginTest::PluginTest(NPP npp, const string& identifier)
+ : m_npp(npp)
+ , m_identifier(identifier)
+{
+ // Reset the shutdown function.
+ shutdownFunction = 0;
+}
+
+PluginTest::~PluginTest()
+{
+}
+
+void PluginTest::NP_Shutdown()
+{
+ if (shutdownFunction)
+ shutdownFunction();
+}
+
+void PluginTest::registerNPShutdownFunction(void (*func)())
+{
+ assert(!shutdownFunction);
+ shutdownFunction = func;
+}
+
+void PluginTest::indicateTestFailure()
+{
+ // This should really be an assert, but there's no way for the test framework
+ // to know that the plug-in process crashed, so we'll just sleep for a while
+ // to ensure that the test times out.
+#if defined(XP_WIN)
+ ::Sleep(100000);
+#else
+ sleep(1000);
+#endif
+}
+
+NPError PluginTest::NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved)
+{
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginTest::NPP_Destroy(NPSavedData**)
+{
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginTest::NPP_SetWindow(NPWindow*)
+{
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginTest::NPP_NewStream(NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)
+{
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginTest::NPP_DestroyStream(NPStream *stream, NPReason reason)
+{
+ return NPERR_NO_ERROR;
+}
+
+int32_t PluginTest::NPP_WriteReady(NPStream*)
+{
+ return 4096;
+}
+
+int32_t PluginTest::NPP_Write(NPStream*, int32_t offset, int32_t len, void* buffer)
+{
+ return len;
+}
+
+int16_t PluginTest::NPP_HandleEvent(void*)
+{
+ return 0;
+}
+
+bool PluginTest::NPP_URLNotify(const char* url, NPReason, void* notifyData)
+{
+ // FIXME: Port the code from NPP_URLNotify in main.cpp over to always using
+ // PluginTest, so we don't have to use a return value to indicate whether the "default" NPP_URLNotify implementation should be invoked.
+ return false;
+}
+
+void PluginTest::NPP_URLRedirectNotify(const char*, int32_t, void* notifyData)
+{
+ NPN_URLRedirectResponse(notifyData, true);
+}
+
+NPError PluginTest::NPP_GetValue(NPPVariable variable, void *value)
+{
+ // We don't know anything about plug-in values so just return NPERR_GENERIC_ERROR.
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError PluginTest::NPP_SetValue(NPNVariable, void *value)
+{
+ return NPERR_GENERIC_ERROR;
+}
+
+// NPN functions.
+
+NPError PluginTest::NPN_GetURL(const char* url, const char* target)
+{
+ return browser->geturl(m_npp, url, target);
+}
+
+NPError PluginTest::NPN_GetURLNotify(const char *url, const char *target, void *notifyData)
+{
+ return browser->geturlnotify(m_npp, url, target, notifyData);
+}
+
+NPError PluginTest::NPN_PostURLNotify(const char *url, const char *target, uint32_t len, const char* buf, NPBool file, void *notifyData)
+{
+ return browser->posturlnotify(m_npp, url, target, len, buf, file, notifyData);
+}
+
+NPError PluginTest::NPN_GetValue(NPNVariable variable, void* value)
+{
+ return browser->getvalue(m_npp, variable, value);
+}
+
+void PluginTest::NPN_InvalidateRect(NPRect* invalidRect)
+{
+ browser->invalidaterect(m_npp, invalidRect);
+}
+
+bool PluginTest::NPN_Invoke(NPObject *npobj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result)
+{
+ return browser->invoke(m_npp, npobj, methodName, args, argCount, result);
+}
+
+void* PluginTest::NPN_MemAlloc(uint32_t size)
+{
+ return browser->memalloc(size);
+}
+
+// NPRuntime NPN functions.
+
+NPIdentifier PluginTest::NPN_GetStringIdentifier(const NPUTF8 *name)
+{
+ return browser->getstringidentifier(name);
+}
+
+NPIdentifier PluginTest::NPN_GetIntIdentifier(int32_t intid)
+{
+ return browser->getintidentifier(intid);
+}
+
+bool PluginTest::NPN_IdentifierIsString(NPIdentifier npIdentifier)
+{
+ return browser->identifierisstring(npIdentifier);
+}
+
+NPUTF8* PluginTest::NPN_UTF8FromIdentifier(NPIdentifier npIdentifier)
+{
+ return browser->utf8fromidentifier(npIdentifier);
+}
+
+int32_t PluginTest::NPN_IntFromIdentifier(NPIdentifier npIdentifier)
+{
+ return browser->intfromidentifier(npIdentifier);
+}
+
+NPObject* PluginTest::NPN_CreateObject(NPClass* npClass)
+{
+ return browser->createobject(m_npp, npClass);
+}
+
+NPObject* PluginTest::NPN_RetainObject(NPObject* npObject)
+{
+ return browser->retainobject(npObject);
+}
+
+void PluginTest::NPN_ReleaseObject(NPObject* npObject)
+{
+ browser->releaseobject(npObject);
+}
+
+bool PluginTest::NPN_RemoveProperty(NPObject* npObject, NPIdentifier propertyName)
+{
+ return browser->removeproperty(m_npp, npObject, propertyName);
+}
+
+void PluginTest::NPN_ReleaseVariantValue(NPVariant* variant)
+{
+ browser->releasevariantvalue(variant);
+}
+
+void PluginTest::NPN_URLRedirectResponse(void* notifyData, NPBool allow)
+{
+ browser->urlredirectresponse(m_npp, notifyData, allow);
+}
+
+#ifdef XP_MACOSX
+bool PluginTest::NPN_ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace)
+{
+ return browser->convertpoint(m_npp, sourceX, sourceY, sourceSpace, destX, destY, destSpace);
+}
+#endif
+
+bool PluginTest::executeScript(const NPString* script, NPVariant* result)
+{
+ NPObject* windowScriptObject;
+ browser->getvalue(m_npp, NPNVWindowNPObject, &windowScriptObject);
+
+ return browser->evaluate(m_npp, windowScriptObject, const_cast<NPString*>(script), result);
+}
+
+void PluginTest::executeScript(const char* script)
+{
+ NPString npScript;
+ npScript.UTF8Characters = script;
+ npScript.UTF8Length = strlen(script);
+
+ NPVariant browserResult;
+ executeScript(&npScript, &browserResult);
+ browser->releasevariantvalue(&browserResult);
+}
+
+WTF_ATTRIBUTE_PRINTF(2, 3)
+void PluginTest::log(const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ pluginLogWithArguments(m_npp, format, args);
+ va_end(args);
+}
+
+NPNetscapeFuncs* PluginTest::netscapeFuncs()
+{
+ return browser;
+}
+
+void PluginTest::waitUntilDone()
+{
+ executeScript("testRunner.waitUntilDone()");
+}
+
+void PluginTest::notifyDone()
+{
+ executeScript("testRunner.notifyDone()");
+}
+
+void PluginTest::registerCreateTestFunction(const string& identifier, CreateTestFunction createTestFunction)
+{
+ assert(!createTestFunctions().count(identifier));
+
+ createTestFunctions()[identifier] = createTestFunction;
+}
+
+std::map<std::string, PluginTest::CreateTestFunction>& PluginTest::createTestFunctions()
+{
+ static std::map<std::string, CreateTestFunction> testFunctions;
+
+ return testFunctions;
+}
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/PluginTest.h b/Tools/DumpRenderTree/TestNetscapePlugIn/PluginTest.h
new file mode 100644
index 000000000..d7a5163ff
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/PluginTest.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PluginTest_h
+#define PluginTest_h
+
+#include <WebKit/npfunctions.h>
+#include <assert.h>
+#include <map>
+#include <string>
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+// Helper classes for implementing has_member
+typedef char (&no_tag)[1];
+typedef char (&yes_tag)[2];
+
+#define DEFINE_HAS_MEMBER_CHECK(member, returnType, argumentTypes) \
+template<typename T, returnType (T::*member) argumentTypes> struct pmf_##member##_helper {}; \
+template<typename T> no_tag has_member_##member##_helper(...); \
+template<typename T> yes_tag has_member_##member##_helper(pmf_##member##_helper<T, &T::member >*); \
+template<typename T> struct has_member_##member { \
+static const bool value = sizeof(has_member_##member##_helper<T>(0)) == sizeof(yes_tag); \
+};
+
+DEFINE_HAS_MEMBER_CHECK(hasMethod, bool, (NPIdentifier methodName));
+DEFINE_HAS_MEMBER_CHECK(invoke, bool, (NPIdentifier methodName, const NPVariant*, uint32_t, NPVariant* result));
+DEFINE_HAS_MEMBER_CHECK(invokeDefault, bool, (const NPVariant*, uint32_t, NPVariant* result));
+DEFINE_HAS_MEMBER_CHECK(hasProperty, bool, (NPIdentifier propertyName));
+DEFINE_HAS_MEMBER_CHECK(getProperty, bool, (NPIdentifier propertyName, NPVariant* result));
+DEFINE_HAS_MEMBER_CHECK(removeProperty, bool, (NPIdentifier propertyName));
+
+class PluginTest {
+public:
+ static PluginTest* create(NPP, const std::string& identifier);
+ virtual ~PluginTest();
+
+ static void NP_Shutdown();
+
+ // NPP functions.
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved);
+ virtual NPError NPP_Destroy(NPSavedData**);
+ virtual NPError NPP_SetWindow(NPWindow*);
+ virtual NPError NPP_NewStream(NPMIMEType, NPStream*, NPBool seekable, uint16_t* stype);
+ virtual NPError NPP_DestroyStream(NPStream*, NPReason);
+ virtual int32_t NPP_WriteReady(NPStream*);
+ virtual int32_t NPP_Write(NPStream*, int32_t offset, int32_t len, void* buffer);
+
+ virtual int16_t NPP_HandleEvent(void* event);
+ virtual bool NPP_URLNotify(const char* url, NPReason, void* notifyData);
+ virtual void NPP_URLRedirectNotify(const char* url, int32_t status, void* notifyData);
+ virtual NPError NPP_GetValue(NPPVariable, void* value);
+ virtual NPError NPP_SetValue(NPNVariable, void *value);
+
+ // NPN functions.
+ NPError NPN_GetURL(const char* url, const char* target);
+ NPError NPN_GetURLNotify(const char* url, const char* target, void* notifyData);
+ NPError NPN_PostURLNotify(const char *url, const char *target, uint32_t len, const char* buf, NPBool file, void *notifyData);
+ NPError NPN_GetValue(NPNVariable, void* value);
+ void NPN_InvalidateRect(NPRect* invalidRect);
+ bool NPN_Invoke(NPObject *, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result);
+ void* NPN_MemAlloc(uint32_t size);
+
+ // NPRuntime NPN functions.
+ NPIdentifier NPN_GetStringIdentifier(const NPUTF8* name);
+ NPIdentifier NPN_GetIntIdentifier(int32_t intid);
+ bool NPN_IdentifierIsString(NPIdentifier);
+ NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier);
+ int32_t NPN_IntFromIdentifier(NPIdentifier);
+
+ NPObject* NPN_CreateObject(NPClass*);
+ NPObject* NPN_RetainObject(NPObject*);
+ void NPN_ReleaseObject(NPObject*);
+ bool NPN_RemoveProperty(NPObject*, NPIdentifier propertyName);
+ void NPN_ReleaseVariantValue(NPVariant*);
+ void NPN_URLRedirectResponse(void* notifyData, NPBool allow);
+
+#ifdef XP_MACOSX
+ bool NPN_ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace);
+#endif
+
+ bool executeScript(const NPString*, NPVariant* result);
+ void executeScript(const char*);
+ void log(const char* format, ...);
+
+ void registerNPShutdownFunction(void (*)());
+
+ static void indicateTestFailure();
+
+ template<typename TestClassTy> class Register {
+ public:
+ Register(const std::string& identifier)
+ {
+ registerCreateTestFunction(identifier, Register::create);
+ }
+
+ private:
+ static PluginTest* create(NPP npp, const std::string& identifier)
+ {
+ return new TestClassTy(npp, identifier);
+ }
+ };
+
+protected:
+ PluginTest(NPP npp, const std::string& identifier);
+
+ // FIXME: A plug-in test shouldn't need to know about it's NPP. Make this private.
+ NPP m_npp;
+
+ const std::string& identifier() const { return m_identifier; }
+
+ static NPNetscapeFuncs* netscapeFuncs();
+
+ void waitUntilDone();
+ void notifyDone();
+
+ // NPObject helper template.
+ template<typename T> struct Object : NPObject {
+ public:
+ static NPObject* create(PluginTest* pluginTest)
+ {
+ Object* object = static_cast<Object*>(pluginTest->NPN_CreateObject(npClass()));
+
+ object->m_pluginTest = pluginTest;
+ return object;
+ }
+
+ // These should never be called.
+ bool hasMethod(NPIdentifier methodName)
+ {
+ assert(false);
+ return false;
+ }
+
+ bool invoke(NPIdentifier methodName, const NPVariant*, uint32_t, NPVariant* result)
+ {
+ assert(false);
+ return false;
+ }
+
+ bool invokeDefault(const NPVariant*, uint32_t, NPVariant* result)
+ {
+ assert(false);
+ return false;
+ }
+
+ bool hasProperty(NPIdentifier propertyName)
+ {
+ assert(false);
+ return false;
+ }
+
+ bool getProperty(NPIdentifier propertyName, NPVariant* result)
+ {
+ assert(false);
+ return false;
+ }
+
+ bool removeProperty(NPIdentifier propertyName)
+ {
+ assert(false);
+ return false;
+ }
+
+ // Helper functions.
+ bool identifierIs(NPIdentifier identifier, const char* value)
+ {
+ return pluginTest()->NPN_GetStringIdentifier(value) == identifier;
+ }
+
+ protected:
+ Object()
+ : m_pluginTest(0)
+ {
+ }
+
+ virtual ~Object()
+ {
+ }
+
+ PluginTest* pluginTest() const { return m_pluginTest; }
+
+ private:
+ static NPObject* NP_Allocate(NPP npp, NPClass* aClass)
+ {
+ return new T;
+ }
+
+ static void NP_Deallocate(NPObject* npObject)
+ {
+ delete static_cast<T*>(npObject);
+ }
+
+ static bool NP_HasMethod(NPObject* npObject, NPIdentifier methodName)
+ {
+ return static_cast<T*>(npObject)->hasMethod(methodName);
+ }
+
+ static bool NP_Invoke(NPObject* npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
+ {
+ return static_cast<T*>(npObject)->invoke(methodName, arguments, argumentCount, result);
+ }
+
+ static bool NP_InvokeDefault(NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
+ {
+ return static_cast<T*>(npObject)->invokeDefault(arguments, argumentCount, result);
+ }
+
+ static bool NP_HasProperty(NPObject* npObject, NPIdentifier propertyName)
+ {
+ return static_cast<T*>(npObject)->hasProperty(propertyName);
+ }
+
+ static bool NP_GetProperty(NPObject* npObject, NPIdentifier propertyName, NPVariant* result)
+ {
+ return static_cast<T*>(npObject)->getProperty(propertyName, result);
+ }
+
+ static bool NP_RemoveProperty(NPObject* npObject, NPIdentifier propertyName)
+ {
+ return static_cast<T*>(npObject)->removeProperty(propertyName);
+ }
+
+ static NPClass* npClass()
+ {
+ static NPClass npClass = {
+ NP_CLASS_STRUCT_VERSION,
+ NP_Allocate,
+ NP_Deallocate,
+ 0, // NPClass::invalidate
+ has_member_hasMethod<T>::value ? NP_HasMethod : 0,
+ has_member_invoke<T>::value ? NP_Invoke : 0,
+ has_member_invokeDefault<T>::value ? NP_InvokeDefault : 0,
+ has_member_hasProperty<T>::value ? NP_HasProperty : 0,
+ has_member_getProperty<T>::value ? NP_GetProperty : 0,
+ 0, // NPClass::setProperty
+ has_member_removeProperty<T>::value ? NP_RemoveProperty : 0,
+ 0, // NPClass::enumerate
+ 0 // NPClass::construct
+ };
+
+ return &npClass;
+ };
+
+ PluginTest* m_pluginTest;
+ };
+
+private:
+ typedef PluginTest* (*CreateTestFunction)(NPP, const std::string&);
+
+ static void registerCreateTestFunction(const std::string&, CreateTestFunction);
+ static std::map<std::string, CreateTestFunction>& createTestFunctions();
+
+ std::string m_identifier;
+};
+
+#endif // PluginTest_h
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/TestObject.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/TestObject.cpp
new file mode 100644
index 000000000..971f9ead3
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/TestObject.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "TestObject.h"
+
+#include "PluginObject.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+static bool testEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count);
+static bool testHasMethod(NPObject*, NPIdentifier name);
+static bool testInvoke(NPObject*, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool testHasProperty(NPObject*, NPIdentifier name);
+static bool testGetProperty(NPObject*, NPIdentifier name, NPVariant*);
+static NPObject *testAllocate(NPP npp, NPClass *theClass);
+static void testDeallocate(NPObject *obj);
+static bool testConstruct(NPObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+
+static NPClass testClass = {
+ NP_CLASS_STRUCT_VERSION,
+ testAllocate,
+ testDeallocate,
+ 0,
+ testHasMethod,
+ testInvoke,
+ 0,
+ testHasProperty,
+ testGetProperty,
+ 0,
+ 0,
+ testEnumerate,
+ testConstruct
+};
+
+NPClass *getTestClass(void)
+{
+ return &testClass;
+}
+
+static int testObjectCount = 0;
+
+int getTestObjectCount()
+{
+ return testObjectCount;
+}
+
+typedef struct {
+ NPObject header;
+ NPObject* testObject;
+} TestObject;
+
+static bool identifiersInitialized = false;
+
+#define NUM_ENUMERATABLE_TEST_IDENTIFIERS 2
+
+enum {
+ ID_PROPERTY_FOO = 0,
+ ID_PROPERTY_BAR,
+ ID_PROPERTY_OBJECT_POINTER,
+ ID_PROPERTY_TEST_OBJECT,
+ ID_PROPERTY_REF_COUNT,
+ NUM_TEST_IDENTIFIERS,
+};
+
+static NPIdentifier testIdentifiers[NUM_TEST_IDENTIFIERS];
+static const NPUTF8 *testIdentifierNames[NUM_TEST_IDENTIFIERS] = {
+ "foo",
+ "bar",
+ "objectPointer",
+ "testObject",
+ "refCount",
+};
+
+#define ID_THROW_EXCEPTION_METHOD 0
+#define NUM_METHOD_IDENTIFIERS 1
+
+static NPIdentifier testMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
+static const NPUTF8 *testMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
+ "throwException",
+};
+
+static void initializeIdentifiers(void)
+{
+ browser->getstringidentifiers(testIdentifierNames, NUM_TEST_IDENTIFIERS, testIdentifiers);
+ browser->getstringidentifiers(testMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, testMethodIdentifiers);
+}
+
+static NPObject* testAllocate(NPP /*npp*/, NPClass* /*theClass*/)
+{
+ TestObject* newInstance = static_cast<TestObject*>(malloc(sizeof(TestObject)));
+ newInstance->testObject = 0;
+ ++testObjectCount;
+
+ if (!identifiersInitialized) {
+ identifiersInitialized = true;
+ initializeIdentifiers();
+ }
+
+ return reinterpret_cast<NPObject*>(newInstance);
+}
+
+static void testDeallocate(NPObject *obj)
+{
+ TestObject* testObject = reinterpret_cast<TestObject*>(obj);
+ if (testObject->testObject)
+ browser->releaseobject(testObject->testObject);
+
+ --testObjectCount;
+ free(obj);
+}
+
+static bool testHasMethod(NPObject*, NPIdentifier name)
+{
+ for (unsigned i = 0; i < NUM_METHOD_IDENTIFIERS; i++) {
+ if (testMethodIdentifiers[i] == name)
+ return true;
+ }
+ return false;
+}
+
+static bool testInvoke(NPObject* header, NPIdentifier name, const NPVariant* /*args*/, uint32_t /*argCount*/, NPVariant* /*result*/)
+{
+ if (name == testMethodIdentifiers[ID_THROW_EXCEPTION_METHOD]) {
+ browser->setexception(header, "test object throwException SUCCESS");
+ return true;
+ }
+ return false;
+}
+
+static bool testHasProperty(NPObject*, NPIdentifier name)
+{
+ for (unsigned i = 0; i < NUM_TEST_IDENTIFIERS; i++) {
+ if (testIdentifiers[i] == name)
+ return true;
+ }
+
+ return false;
+}
+
+static bool testGetProperty(NPObject* npobj, NPIdentifier name, NPVariant* result)
+{
+ if (name == testIdentifiers[ID_PROPERTY_FOO]) {
+ char* mem = static_cast<char*>(browser->memalloc(4));
+ strcpy(mem, "foo");
+ STRINGZ_TO_NPVARIANT(mem, *result);
+ return true;
+ }
+ if (name == testIdentifiers[ID_PROPERTY_OBJECT_POINTER]) {
+ int32_t objectPointer = static_cast<int32_t>(reinterpret_cast<long long>(npobj));
+
+ INT32_TO_NPVARIANT(objectPointer, *result);
+ return true;
+ }
+ if (name == testIdentifiers[ID_PROPERTY_TEST_OBJECT]) {
+ TestObject* testObject = reinterpret_cast<TestObject*>(npobj);
+ if (!testObject->testObject)
+ testObject->testObject = browser->createobject(0, &testClass);
+ browser->retainobject(testObject->testObject);
+ OBJECT_TO_NPVARIANT(testObject->testObject, *result);
+ return true;
+ }
+ if (name == testIdentifiers[ID_PROPERTY_REF_COUNT]) {
+ INT32_TO_NPVARIANT(npobj->referenceCount, *result);
+ return true;
+ }
+
+ return false;
+}
+
+static bool testEnumerate(NPObject* /*npobj*/, NPIdentifier **value, uint32_t *count)
+{
+ *count = NUM_ENUMERATABLE_TEST_IDENTIFIERS;
+
+ *value = (NPIdentifier*)browser->memalloc(NUM_ENUMERATABLE_TEST_IDENTIFIERS * sizeof(NPIdentifier));
+ memcpy(*value, testIdentifiers, sizeof(NPIdentifier) * NUM_ENUMERATABLE_TEST_IDENTIFIERS);
+
+ return true;
+}
+
+static bool testConstruct(NPObject* npobj, const NPVariant* /*args*/, uint32_t /*argCount*/, NPVariant* result)
+{
+ browser->retainobject(npobj);
+
+ // Just return the same object.
+ OBJECT_TO_NPVARIANT(npobj, *result);
+ return true;
+}
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/TestObject.h b/Tools/DumpRenderTree/TestNetscapePlugIn/TestObject.h
new file mode 100644
index 000000000..73748e009
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/TestObject.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <WebKit/npapi.h>
+#include <WebKit/npruntime.h>
+
+NPClass* getTestClass(void);
+int getTestObjectCount();
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/DocumentOpenInDestroyStream.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/DocumentOpenInDestroyStream.cpp
new file mode 100644
index 000000000..69e706ea4
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/DocumentOpenInDestroyStream.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+using namespace std;
+
+extern bool testDocumentOpen(NPP npp);
+
+// Call document.open from NPP_DestroyStream.
+
+class DocumentOpenInDestroyStream : public PluginTest {
+public:
+ DocumentOpenInDestroyStream(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ , m_shouldOpen(true)
+ {
+ }
+
+private:
+ virtual NPError NPP_DestroyStream(NPStream*, NPReason)
+ {
+ if (m_shouldOpen) {
+ testDocumentOpen(m_npp);
+ m_shouldOpen = false;
+ }
+
+ return NPERR_NO_ERROR;
+ }
+
+ bool m_shouldOpen;
+};
+
+static PluginTest::Register<DocumentOpenInDestroyStream> documentOpenInDestroyStream("document-open-in-destroy-stream");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/EvaluateJSAfterRemovingPluginElement.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/EvaluateJSAfterRemovingPluginElement.cpp
new file mode 100644
index 000000000..4b5d3e01d
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/EvaluateJSAfterRemovingPluginElement.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include "PluginObject.h"
+
+using namespace std;
+
+// Executing JS after removing the plugin element from the document should not crash.
+
+class EvaluateJSAfterRemovingPluginElement : public PluginTest {
+public:
+ EvaluateJSAfterRemovingPluginElement(NPP, const string& identifier);
+
+private:
+ virtual NPError NPP_DestroyStream(NPStream*, NPReason);
+
+ bool m_didExecuteScript;
+};
+
+static PluginTest::Register<EvaluateJSAfterRemovingPluginElement> registrar("evaluate-js-after-removing-plugin-element");
+
+EvaluateJSAfterRemovingPluginElement::EvaluateJSAfterRemovingPluginElement(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ , m_didExecuteScript(false)
+{
+ waitUntilDone();
+}
+
+NPError EvaluateJSAfterRemovingPluginElement::NPP_DestroyStream(NPStream*, NPReason)
+{
+ if (m_didExecuteScript)
+ return NPERR_NO_ERROR;
+ m_didExecuteScript = true;
+
+ executeScript("var plugin = document.getElementsByTagName('embed')[0]; plugin.parentElement.removeChild(plugin);");
+ executeScript("document.body.appendChild(document.createTextNode('Executing script after removing the plugin element from the document succeeded.'));");
+ notifyDone();
+
+ return NPERR_NO_ERROR;
+}
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/EvaluateJSWithinNPP_New.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/EvaluateJSWithinNPP_New.cpp
new file mode 100644
index 000000000..c066db59f
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/EvaluateJSWithinNPP_New.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include "PluginObject.h"
+
+using namespace std;
+
+// Executing JS within NPP_New when initializing asynchronously should not be able to deadlock with the WebProcess
+
+class EvaluteJSWithinNPP_New : public PluginTest {
+public:
+ EvaluteJSWithinNPP_New(NPP, const string& identifier);
+
+private:
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData *);
+
+};
+
+EvaluteJSWithinNPP_New::EvaluteJSWithinNPP_New(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+{
+}
+
+NPError EvaluteJSWithinNPP_New::NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData *saved)
+{
+ // Give the WebProcess enough time to be deadlocked waiting for the PluginProcess.
+ usleep(15000);
+ executeScript("var theLocation = window.location;");
+ return NPERR_NO_ERROR;
+}
+
+static PluginTest::Register<EvaluteJSWithinNPP_New> registrar("evalute-js-within-npp-new");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/FormValue.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/FormValue.cpp
new file mode 100644
index 000000000..738e192eb
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/FormValue.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+#include <string.h>
+
+extern NPNetscapeFuncs *browser;
+
+class FormValue : public PluginTest {
+public:
+ FormValue(NPP npp, const std::string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+ virtual NPError NPP_GetValue(NPPVariable, void*);
+};
+
+NPError FormValue::NPP_GetValue(NPPVariable variable, void *value)
+{
+ if (variable == NPPVformValue) {
+ static const char formValueText[] = "Plugin form value";
+ *((void**)value) = browser->memalloc(sizeof(formValueText));
+ if (!*((void**)value))
+ return NPERR_OUT_OF_MEMORY_ERROR;
+ strncpy(*((char**)value), formValueText, sizeof(formValueText));
+ return NPERR_NO_ERROR;
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static PluginTest::Register<FormValue> formValue("form-value");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLNotifyWithURLThatFailsToLoad.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLNotifyWithURLThatFailsToLoad.cpp
new file mode 100644
index 000000000..f6f39ab04
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLNotifyWithURLThatFailsToLoad.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include <string.h>
+
+using namespace std;
+
+// From NPP_New, call NPN_GetURLNotify with a URL that fails to load (NPP_NewStream won't be called).
+// The plug-in should still get a NPP_URLNotify indicating that the load failed.
+static const char *urlThatFailsToLoad = "foo://bar/";
+
+class GetURLNotifyWithURLThatFailsToLoad : public PluginTest {
+public:
+ GetURLNotifyWithURLThatFailsToLoad(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved)
+ {
+ NPN_GetURLNotify(urlThatFailsToLoad, 0, reinterpret_cast<void*>(0x12345678));
+ return NPERR_NO_ERROR;
+ }
+
+ bool NPP_URLNotify(const char* url, NPReason reason, void* notifyData)
+ {
+ bool didFail = false;
+
+ if (strcmp(url, urlThatFailsToLoad))
+ didFail = true;
+
+ if (reason != NPRES_NETWORK_ERR)
+ didFail = true;
+
+ if (notifyData != reinterpret_cast<void*>(0x12345678))
+ didFail = true;
+
+ if (!didFail)
+ executeScript("testSucceeded()");
+ else
+ executeScript("notifyDone()");
+ return true;
+ }
+};
+
+static PluginTest::Register<GetURLNotifyWithURLThatFailsToLoad> getURLWithJavaScriptURLDestroyingPlugin("get-url-notify-with-url-that-fails-to-load");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLWithJavaScriptURL.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLWithJavaScriptURL.cpp
new file mode 100644
index 000000000..b3a72b0d5
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLWithJavaScriptURL.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include <string.h>
+#include <vector>
+
+using namespace std;
+
+const char *javaScriptURL = "javascript:'Hello, ' + 'World!'";
+const char *javaScriptResult = "Hello, World!";
+
+// Test that evaluating a javascript: URL will send a stream with the result of the evaluation.
+// Test that evaluating JavaScript using NPN_GetURL will a stream with result of the evaluation.
+class GetURLWithJavaScriptURL : public PluginTest {
+public:
+ GetURLWithJavaScriptURL(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ , m_didFail(false)
+ {
+ }
+
+private:
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved)
+ {
+ NPN_GetURL(javaScriptURL, 0);
+ return NPERR_NO_ERROR;
+ }
+
+ NPError NPP_NewStream(NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)
+ {
+ stream->pdata = this;
+
+ if (strcmp(stream->url, javaScriptURL))
+ m_didFail = true;
+
+ if (stream->end != strlen(javaScriptResult))
+ m_didFail = true;
+
+ *stype = NP_NORMAL;
+ return NPERR_NO_ERROR;
+ }
+
+ NPError NPP_DestroyStream(NPStream* stream, NPReason reason)
+ {
+ if (stream->pdata != this)
+ m_didFail = true;
+
+ if (reason != NPRES_DONE)
+ m_didFail = true;
+
+ if (m_data.size() != stream->end)
+ m_didFail = true;
+
+ m_data.push_back('\0');
+
+ if (strcmp(&m_data[0], javaScriptResult))
+ m_didFail = true;
+
+ if (!m_didFail)
+ executeScript("testSucceeded()");
+ else
+ executeScript("notifyDone()");
+
+ return NPERR_NO_ERROR;
+ }
+
+ int32_t NPP_WriteReady(NPStream* stream)
+ {
+ if (stream->pdata != this)
+ m_didFail = true;
+
+ return 2;
+ }
+
+ int32_t NPP_Write(NPStream* stream, int32_t offset, int32_t len, void* buffer)
+ {
+ if (stream->pdata != this)
+ m_didFail = true;
+
+ m_data.insert(m_data.end(), static_cast<char*>(buffer), static_cast<char*>(buffer) + len);
+ return len;
+ }
+
+ vector<char> m_data;
+ bool m_didFail;
+};
+
+static PluginTest::Register<GetURLWithJavaScriptURL> getURLWithJavaScriptURLDestroyingPlugin("get-url-with-javascript-url");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLWithJavaScriptURLDestroyingPlugin.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLWithJavaScriptURLDestroyingPlugin.cpp
new file mode 100644
index 000000000..9d63198b1
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetURLWithJavaScriptURLDestroyingPlugin.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+using namespace std;
+
+// From NPP_New, call NPN_GetURL to evaluate JavaScript that destroys the plug-in.
+
+class GetURLWithJavaScriptURLDestroyingPlugin : public PluginTest {
+public:
+ GetURLWithJavaScriptURLDestroyingPlugin(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved)
+ {
+ NPN_GetURL("javascript:removePlugin()", 0);
+ return NPERR_NO_ERROR;
+ }
+};
+
+static PluginTest::Register<GetURLWithJavaScriptURLDestroyingPlugin> getURLWithJavaScriptURLDestroyingPlugin("get-url-with-javascript-url-destroying-plugin");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetUserAgentWithNullNPPFromNPPNew.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetUserAgentWithNullNPPFromNPPNew.cpp
new file mode 100644
index 000000000..887e1521e
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/GetUserAgentWithNullNPPFromNPPNew.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include "PluginObject.h"
+
+using namespace std;
+
+// Trying to get the user agent with a null instance from NPP_New.
+
+class GetUserAgentWithNullNPPFromNPPNew : public PluginTest {
+public:
+ GetUserAgentWithNullNPPFromNPPNew(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved)
+ {
+ if (!browser->uagent(0))
+ pluginLog(m_npp, "FAILURE: Null user agent returned.");
+ else
+ pluginLog(m_npp, "SUCCESS!");
+
+ return NPERR_NO_ERROR;
+ }
+
+};
+
+static PluginTest::Register<GetUserAgentWithNullNPPFromNPPNew> getUserAgentWithNullNPPFromNPPNew("get-user-agent-with-null-npp-from-npp-new");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/InvokeDestroysPluginWithinNPP_New.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/InvokeDestroysPluginWithinNPP_New.cpp
new file mode 100644
index 000000000..0e2dbdce7
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/InvokeDestroysPluginWithinNPP_New.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include "PluginObject.h"
+
+using namespace std;
+
+// Executing JS within NPP_New when initializing asynchronously should not be able to deadlock with the WebProcess
+
+class InvokeDestroysPluginWithinNPP_New : public PluginTest {
+public:
+ InvokeDestroysPluginWithinNPP_New(NPP, const string& identifier);
+
+private:
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData *);
+
+};
+
+InvokeDestroysPluginWithinNPP_New::InvokeDestroysPluginWithinNPP_New(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+{
+}
+
+NPError InvokeDestroysPluginWithinNPP_New::NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData *saved)
+{
+ // Give the WebProcess enough time to be deadlocked waiting for the PluginProcess if things aren't working correctly.
+ usleep(15000);
+
+ NPObject* windowObject = 0;
+ if (NPN_GetValue(NPNVWindowNPObject, &windowObject) != NPERR_NO_ERROR)
+ return NPERR_GENERIC_ERROR;
+
+ if (!windowObject)
+ return NPERR_GENERIC_ERROR;
+
+ NPVariant result;
+ if (!NPN_Invoke(windowObject, NPN_GetStringIdentifier("removePluginElement"), 0, 0, &result))
+ return NPERR_GENERIC_ERROR;
+
+ return NPERR_NO_ERROR;
+}
+
+static PluginTest::Register<InvokeDestroysPluginWithinNPP_New> registrar("invoke-destroys-plugin-within-npp-new");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/LogNPPSetWindow.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/LogNPPSetWindow.cpp
new file mode 100644
index 000000000..4b1943730
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/LogNPPSetWindow.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+using namespace std;
+
+class LogNPPSetWindow : public PluginTest {
+public:
+ LogNPPSetWindow(NPP, const string& identifier);
+
+private:
+ virtual NPError NPP_SetWindow(NPWindow*);
+};
+
+LogNPPSetWindow::LogNPPSetWindow(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+{
+}
+
+NPError LogNPPSetWindow::NPP_SetWindow(NPWindow* window)
+{
+ char message[1024];
+ snprintf(message, 1024, "NPP_SetWindow: %s window, Rect {%i, %i, %i, %i}, Clip Rect {%i, %i, %i, %i}, Type %i",
+ window->window ? "non-NULL" : "NULL", window->x, window->y, window->width, window->height,
+ window->clipRect.left, window->clipRect.top, window->clipRect.right, window->clipRect.bottom,
+ window->type);
+
+ char script[1536];
+ snprintf(script, 1536, "window.setTimeout('windowWasSet(\"%s\");', 0);", message);
+
+ executeScript(script);
+
+ return NPERR_NO_ERROR;
+}
+
+static PluginTest::Register<LogNPPSetWindow> registrar("log-npp-set-window");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPDeallocateCalledBeforeNPShutdown.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPDeallocateCalledBeforeNPShutdown.cpp
new file mode 100644
index 000000000..91f47aff1
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPDeallocateCalledBeforeNPShutdown.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+using namespace std;
+
+static bool wasShutdownCalled = false;
+
+class NPDeallocateCalledBeforeNPShutdown : public PluginTest {
+public:
+ NPDeallocateCalledBeforeNPShutdown(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+ // This is the test object.
+ class TestObject : public Object<TestObject> {
+ public:
+ ~TestObject()
+ {
+ if (wasShutdownCalled)
+ indicateTestFailure();
+ }
+ };
+
+ // This is the scriptable object. It has a single "testObject" property.
+ class ScriptableObject : public Object<ScriptableObject> {
+ public:
+ bool hasProperty(NPIdentifier propertyName)
+ {
+ return propertyName == pluginTest()->NPN_GetStringIdentifier("testObject");
+ }
+
+ bool getProperty(NPIdentifier propertyName, NPVariant* result)
+ {
+ if (propertyName != pluginTest()->NPN_GetStringIdentifier("testObject"))
+ return false;
+
+ NPObject* testObject = TestObject::create(pluginTest());
+ OBJECT_TO_NPVARIANT(testObject, *result);
+ return true;
+ }
+ };
+
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved)
+ {
+ registerNPShutdownFunction(shutdown);
+
+ return NPERR_NO_ERROR;
+ }
+
+ virtual NPError NPP_GetValue(NPPVariable variable, void *value)
+ {
+ if (variable != NPPVpluginScriptableNPObject)
+ return NPERR_GENERIC_ERROR;
+
+ *(NPObject**)value = ScriptableObject::create(this);
+
+ return NPERR_NO_ERROR;
+ }
+
+ static void shutdown()
+ {
+ wasShutdownCalled = true;
+ }
+
+};
+
+static PluginTest::Register<NPDeallocateCalledBeforeNPShutdown> npRuntimeObjectFromDestroyedPlugin("np-deallocate-called-before-np-shutdown");
+
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPPNewFails.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPPNewFails.cpp
new file mode 100644
index 000000000..e3b11d629
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPPNewFails.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include <string.h>
+
+using namespace std;
+
+class NPPNewFails : public PluginTest {
+public:
+ NPPNewFails(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData *saved)
+ {
+ return NPERR_GENERIC_ERROR;
+ }
+};
+
+static PluginTest::Register<NPPNewFails> nppNewFails("npp-new-fails");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPPSetWindowCalledDuringDestruction.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPPSetWindowCalledDuringDestruction.cpp
new file mode 100644
index 000000000..50af95905
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPPSetWindowCalledDuringDestruction.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include "PluginObject.h"
+
+using namespace std;
+
+// NPP_SetWindow should be called with a null window handle as destruction begins on non-Mac platforms.
+
+class NPPSetWindowCalledDuringDestruction : public PluginTest {
+public:
+ NPPSetWindowCalledDuringDestruction(NPP, const string& identifier);
+
+ void setWillBeDestroyed() { m_willBeDestroyed = true; }
+
+private:
+ struct ScriptObject : Object<ScriptObject> {
+ bool hasMethod(NPIdentifier);
+ bool invoke(NPIdentifier, const NPVariant*, uint32_t, NPVariant*);
+ };
+
+ virtual NPError NPP_GetValue(NPPVariable, void*);
+ virtual NPError NPP_SetWindow(NPWindow*);
+ virtual NPError NPP_Destroy(NPSavedData**);
+
+ bool m_willBeDestroyed;
+ bool m_setWindowCalledBeforeDestruction;
+ bool m_setWindowCalledDuringDestruction;
+};
+
+static PluginTest::Register<NPPSetWindowCalledDuringDestruction> registrar("npp-set-window-called-during-destruction");
+
+NPPSetWindowCalledDuringDestruction::NPPSetWindowCalledDuringDestruction(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ , m_willBeDestroyed(false)
+ , m_setWindowCalledBeforeDestruction(false)
+ , m_setWindowCalledDuringDestruction(false)
+{
+}
+
+NPError NPPSetWindowCalledDuringDestruction::NPP_GetValue(NPPVariable variable, void* value)
+{
+ if (variable != NPPVpluginScriptableNPObject)
+ return NPERR_GENERIC_ERROR;
+
+ *static_cast<NPObject**>(value) = ScriptObject::create(this);
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPPSetWindowCalledDuringDestruction::NPP_SetWindow(NPWindow* window)
+{
+ if (m_willBeDestroyed) {
+ m_setWindowCalledDuringDestruction = true;
+ if (!m_setWindowCalledBeforeDestruction) {
+ log("Fail: setWillBeDestroyed() was called before the initial NPP_SetWindow call");
+ return NPERR_NO_ERROR;
+ }
+#ifndef XP_MACOSX
+ if (window->window)
+ log("Fail: NPP_SetWindow passed a non-null window during plugin destruction");
+#endif
+ return NPERR_NO_ERROR;
+ }
+
+ if (m_setWindowCalledBeforeDestruction) {
+ log("Fail: NPP_SetWindow called more than once before plugin destruction");
+ return NPERR_NO_ERROR;
+ }
+
+ m_setWindowCalledBeforeDestruction = true;
+ return NPERR_NO_ERROR;
+}
+
+NPError NPPSetWindowCalledDuringDestruction::NPP_Destroy(NPSavedData**)
+{
+#ifdef XP_MACOSX
+ bool shouldHaveBeenCalledDuringDestruction = false;
+#else
+ bool shouldHaveBeenCalledDuringDestruction = true;
+#endif
+
+ if (m_setWindowCalledDuringDestruction == shouldHaveBeenCalledDuringDestruction)
+ log("Success: NPP_SetWindow %s called during plugin destruction", shouldHaveBeenCalledDuringDestruction ? "was" : "was not");
+ else
+ log("Fail: NPP_SetWindow %s called during plugin destruction", shouldHaveBeenCalledDuringDestruction ? "was not" : "was");
+
+ return NPERR_NO_ERROR;
+}
+
+bool NPPSetWindowCalledDuringDestruction::ScriptObject::hasMethod(NPIdentifier methodName)
+{
+ return methodName == pluginTest()->NPN_GetStringIdentifier("setWillBeDestroyed");
+}
+
+bool NPPSetWindowCalledDuringDestruction::ScriptObject::invoke(NPIdentifier identifier, const NPVariant*, uint32_t, NPVariant*)
+{
+ assert(identifier == pluginTest()->NPN_GetStringIdentifier("setWillBeDestroyed"));
+ static_cast<NPPSetWindowCalledDuringDestruction*>(pluginTest())->setWillBeDestroyed();
+ return true;
+}
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeCallsWithNullNPP.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeCallsWithNullNPP.cpp
new file mode 100644
index 000000000..84e9714df
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeCallsWithNullNPP.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+class NPRuntimeCallsWithNullNPP : public PluginTest {
+public:
+ NPRuntimeCallsWithNullNPP(NPP npp, const std::string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData *saved)
+ {
+ NPObject* windowObject = 0;
+ if (NPN_GetValue(NPNVWindowNPObject, &windowObject) != NPERR_NO_ERROR || !windowObject)
+ return NPERR_GENERIC_ERROR;
+
+ NPIdentifier alertIdentifier = NPN_GetStringIdentifier("alert");
+ if (!PluginTest::netscapeFuncs()->hasmethod(0, windowObject, alertIdentifier)) {
+ NPN_ReleaseObject(windowObject);
+ return NPERR_GENERIC_ERROR;
+ }
+
+ NPIdentifier documentIdentifier = NPN_GetStringIdentifier("document");
+ NPVariant variant;
+ if (!PluginTest::netscapeFuncs()->getproperty(0, windowObject, documentIdentifier, &variant)) {
+ NPN_ReleaseObject(windowObject);
+ return NPERR_GENERIC_ERROR;
+ }
+ NPN_ReleaseVariantValue(&variant);
+
+ NPN_ReleaseObject(windowObject);
+
+ executeScript("document.getElementById('result').innerHTML = 'SUCCESS!'");
+ notifyDone();
+ return NPERR_NO_ERROR;
+ }
+};
+
+static PluginTest::Register<NPRuntimeCallsWithNullNPP> npRuntimeCallsWithNullNPP("npruntime-calls-with-null-npp");
+
+
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeObjectFromDestroyedPlugin.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeObjectFromDestroyedPlugin.cpp
new file mode 100644
index 000000000..a9a5962dd
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeObjectFromDestroyedPlugin.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+using namespace std;
+
+class NPRuntimeObjectFromDestroyedPlugin : public PluginTest {
+public:
+ NPRuntimeObjectFromDestroyedPlugin(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+ // This is the test object.
+ class TestObject : public Object<TestObject> { };
+
+ // This is the scriptable object. It has a single "testObject" property and an "evaluate" function.
+ class ScriptableObject : public Object<ScriptableObject> {
+ public:
+ bool hasMethod(NPIdentifier methodName)
+ {
+ return identifierIs(methodName, "evaluate");
+ }
+
+ bool invoke(NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result)
+ {
+ if (!identifierIs(methodName, "evaluate"))
+ return false;
+
+ if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
+ return false;
+
+ return pluginTest()->executeScript(&NPVARIANT_TO_STRING(args[0]), result);
+ }
+
+ bool hasProperty(NPIdentifier propertyName)
+ {
+ return identifierIs(propertyName, "testObject");
+ }
+
+ bool getProperty(NPIdentifier propertyName, NPVariant* result)
+ {
+ if (propertyName != pluginTest()->NPN_GetStringIdentifier("testObject"))
+ return false;
+
+ NPObject* testObject = TestObject::create(pluginTest());
+ OBJECT_TO_NPVARIANT(testObject, *result);
+ return true;
+ }
+ };
+
+ virtual NPError NPP_GetValue(NPPVariable variable, void *value)
+ {
+ if (variable != NPPVpluginScriptableNPObject)
+ return NPERR_GENERIC_ERROR;
+
+ *(NPObject**)value = ScriptableObject::create(this);
+
+ return NPERR_NO_ERROR;
+ }
+};
+
+static PluginTest::Register<NPRuntimeObjectFromDestroyedPlugin> npRuntimeObjectFromDestroyedPlugin("npruntime-object-from-destroyed-plugin");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeRemoveProperty.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeRemoveProperty.cpp
new file mode 100644
index 000000000..8a923dc23
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NPRuntimeRemoveProperty.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include <string.h>
+
+using namespace std;
+
+class NPRuntimeRemoveProperty : public PluginTest {
+public:
+ NPRuntimeRemoveProperty(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+ struct TestObject : Object<TestObject> {
+ public:
+ TestObject()
+ : m_lastRemovedProperty(0)
+ {
+ }
+
+ bool hasProperty(NPIdentifier propertyName)
+ {
+ if (identifierIs(propertyName, "lastRemovedProperty"))
+ return true;
+
+ return false;
+ }
+
+ bool getProperty(NPIdentifier propertyName, NPVariant* result)
+ {
+ assert(identifierIs(propertyName, "lastRemovedProperty"));
+
+ if (!m_lastRemovedProperty)
+ return false;
+
+ if (pluginTest()->NPN_IdentifierIsString(m_lastRemovedProperty)) {
+ char* lastRemovedPropertyName = pluginTest()->NPN_UTF8FromIdentifier(m_lastRemovedProperty);
+
+ STRINGZ_TO_NPVARIANT(lastRemovedPropertyName, *result);
+ return true;
+ }
+
+ int intIdentifier = pluginTest()->NPN_IntFromIdentifier(m_lastRemovedProperty);
+ DOUBLE_TO_NPVARIANT(intIdentifier, *result);
+ return true;
+ }
+
+ bool removeProperty(NPIdentifier propertyName)
+ {
+ m_lastRemovedProperty = propertyName;
+ return true;
+ }
+
+ private:
+ NPIdentifier m_lastRemovedProperty;
+ };
+
+ struct PluginObject : Object<PluginObject> {
+ public:
+ PluginObject()
+ : m_testObject(0)
+ {
+ }
+
+ ~PluginObject()
+ {
+ if (m_testObject)
+ pluginTest()->NPN_ReleaseObject(m_testObject);
+ }
+
+ bool hasMethod(NPIdentifier methodName)
+ {
+ if (identifierIs(methodName, "testRemoveProperty"))
+ return true;
+
+ return false;
+ }
+
+ bool invoke(NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
+ {
+ assert(identifierIs(methodName, "testRemoveProperty"));
+
+ if (argumentCount != 2)
+ return false;
+
+ if (!NPVARIANT_IS_OBJECT(arguments[0]))
+ return false;
+
+ if (!NPVARIANT_IS_STRING(arguments[1]) && !NPVARIANT_IS_DOUBLE(arguments[1]))
+ return false;
+
+ NPIdentifier propertyName;
+ if (NPVARIANT_IS_STRING(arguments[1])) {
+ string propertyNameString(arguments[1].value.stringValue.UTF8Characters,
+ arguments[1].value.stringValue.UTF8Length);
+
+ propertyName = pluginTest()->NPN_GetStringIdentifier(propertyNameString.c_str());
+ } else {
+ int32_t number = static_cast<int32_t>(arguments[1].value.doubleValue);
+ propertyName = pluginTest()->NPN_GetIntIdentifier(number);
+ }
+
+ pluginTest()->NPN_RemoveProperty(NPVARIANT_TO_OBJECT(arguments[0]), propertyName);
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ }
+
+ bool hasProperty(NPIdentifier propertyName)
+ {
+ if (identifierIs(propertyName, "testObject"))
+ return true;
+
+ return false;
+ }
+
+ bool getProperty(NPIdentifier propertyName, NPVariant* result)
+ {
+ assert(identifierIs(propertyName, "testObject"));
+
+ if (!m_testObject)
+ m_testObject = TestObject::create(pluginTest());
+
+ OBJECT_TO_NPVARIANT(pluginTest()->NPN_RetainObject(m_testObject), *result);
+ return true;
+ }
+
+ private:
+ NPObject* m_testObject;
+ };
+
+ virtual NPError NPP_GetValue(NPPVariable variable, void *value)
+ {
+ if (variable != NPPVpluginScriptableNPObject)
+ return NPERR_GENERIC_ERROR;
+
+ *(NPObject**)value = PluginObject::create(this);
+
+ return NPERR_NO_ERROR;
+ }
+
+};
+
+static PluginTest::Register<NPRuntimeRemoveProperty> npRuntimeRemoveProperty("npruntime-remove-property");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NullNPPGetValuePointer.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NullNPPGetValuePointer.cpp
new file mode 100644
index 000000000..9e4e976f0
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/NullNPPGetValuePointer.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include "PluginObject.h"
+
+using namespace std;
+
+// Passing null for our NPP_GetValue function pointer should not crash.
+
+class NullNPPGetValuePointer : public PluginTest {
+public:
+ NullNPPGetValuePointer(NPP, const string& identifier);
+
+private:
+ virtual NPError NPP_Destroy(NPSavedData**);
+ virtual NPError NPP_GetValue(NPPVariable, void* value);
+
+ NPP_GetValueProcPtr m_originalNPPGetValuePointer;
+};
+
+static PluginTest::Register<NullNPPGetValuePointer> registrar("null-npp-getvalue-pointer");
+
+NullNPPGetValuePointer::NullNPPGetValuePointer(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ , m_originalNPPGetValuePointer(pluginFunctions->getvalue)
+{
+ // Be sneaky and null out the getvalue pointer the browser is holding. This simulates a plugin
+ // that doesn't implement NPP_GetValue (like Shockwave Director 10.3 on Windows). Note that if
+ // WebKit copies the NPPluginFuncs struct this technique will have no effect and WebKit will
+ // call into our NPP_GetValue implementation.
+ pluginFunctions->getvalue = 0;
+}
+
+NPError NullNPPGetValuePointer::NPP_Destroy(NPSavedData**)
+{
+ // Set the NPP_GetValue pointer back the way it was before we mucked with it so we don't mess
+ // up future uses of the plugin module.
+ pluginFunctions->getvalue = m_originalNPPGetValuePointer;
+ return NPERR_NO_ERROR;
+}
+
+NPError NullNPPGetValuePointer::NPP_GetValue(NPPVariable, void*)
+{
+ pluginLog(m_npp, "NPP_GetValue was called but should not have been. Maybe WebKit copied the NPPluginFuncs struct, which would invalidate this test.");
+ return NPERR_GENERIC_ERROR;
+}
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PassDifferentNPPStruct.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PassDifferentNPPStruct.cpp
new file mode 100644
index 000000000..d78a29bae
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PassDifferentNPPStruct.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include "PluginObject.h"
+
+using namespace std;
+
+// Passing a different NPP struct that has the same ndata value as the one passed to NPP_New should
+// not trigger an assertion failure.
+
+class PassDifferentNPPStruct : public PluginTest {
+public:
+ PassDifferentNPPStruct(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ , m_didReceiveInitialSetWindowCall(false)
+ {
+ }
+
+private:
+ virtual NPError NPP_SetWindow(NPWindow* window)
+ {
+ if (m_didReceiveInitialSetWindowCall)
+ return NPERR_NO_ERROR;
+ m_didReceiveInitialSetWindowCall = true;
+
+ NPP oldNPP = m_npp;
+ NPP_t differentNPP = *m_npp;
+ m_npp = &differentNPP;
+
+ NPBool privateMode;
+ NPError error = NPN_GetValue(NPNVprivateModeBool, &privateMode);
+
+ m_npp = oldNPP;
+
+ if (error != NPERR_NO_ERROR) {
+ log("NPN_GetValue(NPNVprivateModeBool) with a different NPP struct failed with error %d", error);
+ notifyDone();
+ return NPERR_GENERIC_ERROR;
+ }
+ log("NPN_GetValue(NPNVprivateModeBool) with a different NPP struct succeeded");
+ notifyDone();
+ return NPERR_NO_ERROR;
+ }
+
+ bool m_didReceiveInitialSetWindowCall;
+};
+
+static PluginTest::Register<PassDifferentNPPStruct> getValueNetscapeWindow("pass-different-npp-struct");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PluginScriptableNPObjectInvokeDefault.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PluginScriptableNPObjectInvokeDefault.cpp
new file mode 100644
index 000000000..959e182e4
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PluginScriptableNPObjectInvokeDefault.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+using namespace std;
+
+// A test where the plug-ins scriptable object either has or doesn't have an invokeDefault function.
+class PluginScriptableNPObjectInvokeDefault : public PluginTest {
+public:
+ PluginScriptableNPObjectInvokeDefault(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+ struct NPObjectWithoutInvokeDefault : Object<NPObjectWithoutInvokeDefault> { };
+
+ struct NPObjectWithInvokeDefault : Object<NPObjectWithInvokeDefault> {
+ public:
+ bool invokeDefault(const NPVariant*, uint32_t, NPVariant* result)
+ {
+ INT32_TO_NPVARIANT(1, *result);
+ return true;
+ }
+ };
+
+ virtual NPError NPP_GetValue(NPPVariable variable, void *value)
+ {
+ if (variable != NPPVpluginScriptableNPObject)
+ return NPERR_GENERIC_ERROR;
+
+ NPObject* object;
+ if (identifier() == "plugin-scriptable-npobject-invoke-default")
+ object = NPObjectWithInvokeDefault::create(this);
+ else
+ object = NPObjectWithoutInvokeDefault::create(this);
+
+ *(NPObject**)value = object;
+
+ return NPERR_NO_ERROR;
+ }
+};
+
+static PluginTest::Register<PluginScriptableNPObjectInvokeDefault> pluginScriptableNPObjectInvokeDefault("plugin-scriptable-npobject-invoke-default");
+static PluginTest::Register<PluginScriptableNPObjectInvokeDefault> pluginScriptableNPObjectNoInvokeDefault("plugin-scriptable-npobject-no-invoke-default");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PluginScriptableObjectOverridesAllProperties.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PluginScriptableObjectOverridesAllProperties.cpp
new file mode 100644
index 000000000..ca399a816
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PluginScriptableObjectOverridesAllProperties.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include <string.h>
+
+using namespace std;
+
+class PluginScriptableObjectOverridesAllProperties : public PluginTest {
+public:
+ PluginScriptableObjectOverridesAllProperties(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+ class PluginObject : public Object<PluginObject> {
+ public:
+ PluginObject()
+ {
+ }
+
+ ~PluginObject()
+ {
+ }
+
+ bool hasProperty(NPIdentifier propertyName)
+ {
+ return true;
+ }
+
+ bool getProperty(NPIdentifier propertyName, NPVariant* result)
+ {
+ static const char* message = "My name is ";
+ char* propertyString = pluginTest()->NPN_UTF8FromIdentifier(propertyName);
+
+ int bufferLength = strlen(propertyString) + strlen(message) + 1;
+ char* resultBuffer = static_cast<char*>(pluginTest()->NPN_MemAlloc(bufferLength));
+ snprintf(resultBuffer, bufferLength, "%s%s", message, propertyString);
+
+ STRINGZ_TO_NPVARIANT(resultBuffer, *result);
+
+ return true;
+ }
+ };
+
+ virtual NPError NPP_GetValue(NPPVariable variable, void *value)
+ {
+ if (variable != NPPVpluginScriptableNPObject)
+ return NPERR_GENERIC_ERROR;
+
+ *(NPObject**)value = PluginObject::create(this);
+
+ return NPERR_NO_ERROR;
+ }
+
+};
+
+static PluginTest::Register<PluginScriptableObjectOverridesAllProperties> pluginScriptableObjectOverridesAllProperties("plugin-scriptable-object-overrides-all-properties");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PrivateBrowsing.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PrivateBrowsing.cpp
new file mode 100644
index 000000000..ea5ac1ccc
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/PrivateBrowsing.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+using namespace std;
+
+class PrivateBrowsing : public PluginTest {
+public:
+ PrivateBrowsing(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ , m_cachedPrivateBrowsingEnabled(false)
+ {
+ }
+
+private:
+ bool privateBrowsingEnabled()
+ {
+ NPBool privateBrowsingEnabled = FALSE;
+ NPN_GetValue(NPNVprivateModeBool, &privateBrowsingEnabled);
+ return privateBrowsingEnabled;
+ }
+
+ bool cachedPrivateBrowsingEnabled()
+ {
+ return m_cachedPrivateBrowsingEnabled;
+ }
+
+ class ScriptableObject : public Object<ScriptableObject> {
+ public:
+ bool hasProperty(NPIdentifier propertyName)
+ {
+ return identifierIs(propertyName, "privateBrowsingEnabled")
+ || identifierIs(propertyName, "cachedPrivateBrowsingEnabled");
+ }
+
+ bool getProperty(NPIdentifier propertyName, NPVariant* result)
+ {
+ if (identifierIs(propertyName, "privateBrowsingEnabled")) {
+ BOOLEAN_TO_NPVARIANT(pluginTest()->privateBrowsingEnabled(), *result);
+ return true;
+ }
+ if (identifierIs(propertyName, "cachedPrivateBrowsingEnabled")) {
+ BOOLEAN_TO_NPVARIANT(pluginTest()->cachedPrivateBrowsingEnabled(), *result);
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ PrivateBrowsing* pluginTest() const { return static_cast<PrivateBrowsing*>(Object<ScriptableObject>::pluginTest()); }
+ };
+
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData *saved)
+ {
+ m_cachedPrivateBrowsingEnabled = privateBrowsingEnabled();
+ return NPERR_NO_ERROR;
+ }
+
+ virtual NPError NPP_GetValue(NPPVariable variable, void* value)
+ {
+ if (variable != NPPVpluginScriptableNPObject)
+ return NPERR_GENERIC_ERROR;
+
+ *(NPObject**)value = ScriptableObject::create(this);
+
+ return NPERR_NO_ERROR;
+ }
+
+ virtual NPError NPP_SetValue(NPNVariable variable, void* value)
+ {
+ switch (variable) {
+ case NPNVprivateModeBool:
+ m_cachedPrivateBrowsingEnabled = *(NPBool*)value;
+ return NPERR_NO_ERROR;
+ default:
+ return NPERR_GENERIC_ERROR;
+ }
+
+ }
+ bool m_cachedPrivateBrowsingEnabled;
+};
+
+static PluginTest::Register<PrivateBrowsing> privateBrowsing("private-browsing");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/SlowNPPNew.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/SlowNPPNew.cpp
new file mode 100644
index 000000000..8c80d55a5
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/SlowNPPNew.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include <string.h>
+
+using namespace std;
+
+class SlowNPPNew : public PluginTest {
+public:
+ SlowNPPNew(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+ class PluginObject : public Object<PluginObject> {
+ public:
+ PluginObject()
+ {
+ }
+
+ ~PluginObject()
+ {
+ }
+
+ bool hasProperty(NPIdentifier propertyName)
+ {
+ return true;
+ }
+
+ bool getProperty(NPIdentifier propertyName, NPVariant* result)
+ {
+ static const char* message = "My name is ";
+ char* propertyString = pluginTest()->NPN_UTF8FromIdentifier(propertyName);
+
+ int bufferLength = strlen(propertyString) + strlen(message) + 1;
+ char* resultBuffer = static_cast<char*>(pluginTest()->NPN_MemAlloc(bufferLength));
+ snprintf(resultBuffer, bufferLength, "%s%s", message, propertyString);
+
+ STRINGZ_TO_NPVARIANT(resultBuffer, *result);
+
+ return true;
+ }
+ };
+
+ virtual NPError NPP_GetValue(NPPVariable variable, void *value)
+ {
+ if (variable != NPPVpluginScriptableNPObject)
+ return NPERR_GENERIC_ERROR;
+
+ *(NPObject**)value = PluginObject::create(this);
+
+ return NPERR_NO_ERROR;
+ }
+
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData *saved)
+ {
+ usleep(550000);
+ return NPERR_NO_ERROR;
+ }
+};
+
+static PluginTest::Register<SlowNPPNew> slowNPPNew("slow-npp-new");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/ToStringAndValueOfObject.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/ToStringAndValueOfObject.cpp
new file mode 100644
index 000000000..e46d1416b
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/ToStringAndValueOfObject.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013 Cisco Systems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include <string.h>
+
+using namespace std;
+
+class ToStringAndValueOfObject : public PluginTest {
+public:
+ ToStringAndValueOfObject(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+ class ScriptableObject : public Object<ScriptableObject> {
+ public:
+ bool hasMethod(NPIdentifier methodName)
+ {
+ return identifierIs(methodName, "toString")
+ || identifierIs(methodName, "valueOf");
+ }
+
+ bool invoke(NPIdentifier methodName, const NPVariant*, uint32_t, NPVariant* result)
+ {
+ if (identifierIs(methodName, "toString")) {
+ // Return classname as string.
+ char* className = static_cast<char*>(pluginTest()->NPN_MemAlloc(11));
+ strcpy(className, "TestObject");
+ STRINGZ_TO_NPVARIANT(className, *result);
+ return true;
+ }
+ if (identifierIs(methodName, "valueOf")) {
+ // Return some number.
+ DOUBLE_TO_NPVARIANT(123456789, *result);
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ ToStringAndValueOfObject* pluginTest() const { return static_cast<ToStringAndValueOfObject*>(Object<ScriptableObject>::pluginTest()); }
+ };
+
+ virtual NPError NPP_GetValue(NPPVariable variable, void* value)
+ {
+ if (variable != NPPVpluginScriptableNPObject)
+ return NPERR_GENERIC_ERROR;
+
+ *(NPObject**)value = ScriptableObject::create(this);
+
+ return NPERR_NO_ERROR;
+ }
+};
+
+static PluginTest::Register<ToStringAndValueOfObject> ToStringAndValueOfObject("to-string-and-value-of-object");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/URLRedirect.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/URLRedirect.cpp
new file mode 100644
index 000000000..b834703da
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/URLRedirect.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include <string.h>
+
+using namespace std;
+
+class URLRedirect : public PluginTest {
+public:
+ URLRedirect(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+ struct Redirect {
+ int redirectsRemaining;
+ bool async;
+ bool hasFired;
+ };
+
+ std::map<void*, Redirect> redirects;
+
+private:
+ // This is the test object.
+ class TestObject : public Object<TestObject> { };
+
+ // This is the scriptable object. It has a single "testObject" property and an "evaluate" function.
+ class ScriptableObject : public Object<ScriptableObject> {
+ public:
+ bool hasMethod(NPIdentifier methodName)
+ {
+ return identifierIs(methodName, "get") || identifierIs(methodName, "getAsync") || identifierIs(methodName, "serviceAsync");
+ }
+
+ bool get(const NPVariant* args, uint32_t argCount, NPVariant* result, bool async)
+ {
+ if (argCount != 3 || !NPVARIANT_IS_STRING(args[0]) || !(NPVARIANT_IS_BOOLEAN(args[1]) || NPVARIANT_IS_DOUBLE(args[1]) || NPVARIANT_IS_INT32(args[1])) || !NPVARIANT_IS_STRING(args[2]))
+ return false;
+
+ const NPString* notifyString = &NPVARIANT_TO_STRING(args[2]);
+ basic_string<NPUTF8> notify(notifyString->UTF8Characters, notifyString->UTF8Length);
+ NPIdentifier notifyMethod = pluginTest()->NPN_GetStringIdentifier(notify.c_str());
+
+ Redirect& redirect = static_cast<URLRedirect*>(pluginTest())->redirects[reinterpret_cast<void*>(notifyMethod)];
+ if (NPVARIANT_IS_DOUBLE(args[1]))
+ redirect.redirectsRemaining = NPVARIANT_TO_DOUBLE(args[1]);
+ else if (NPVARIANT_IS_INT32(args[1]))
+ redirect.redirectsRemaining = NPVARIANT_TO_INT32(args[1]);
+ else if (NPVARIANT_IS_BOOLEAN(args[1]))
+ redirect.redirectsRemaining = NPVARIANT_TO_BOOLEAN(args[1]);
+ redirect.async = async;
+ redirect.hasFired = true;
+
+ const NPString* urlString = &NPVARIANT_TO_STRING(args[0]);
+ basic_string<NPUTF8> url(urlString->UTF8Characters, urlString->UTF8Length);
+
+ pluginTest()->NPN_GetURLNotify(url.c_str(), 0, reinterpret_cast<void*>(notifyMethod));
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ }
+
+ bool serviceAsync(const NPVariant* args, uint32_t argCount, NPVariant* result)
+ {
+ if (argCount)
+ return false;
+
+ NPBool seen = 0;
+ URLRedirect* plugin = static_cast<URLRedirect*>(pluginTest());
+ for (auto& redirect : plugin->redirects) {
+ if (redirect.second.hasFired)
+ continue;
+ redirect.second.hasFired = true;
+ plugin->NPN_URLRedirectResponse(redirect.first, redirect.second.redirectsRemaining);
+ if (redirect.second.redirectsRemaining)
+ --redirect.second.redirectsRemaining;
+ seen = 1;
+ }
+
+ BOOLEAN_TO_NPVARIANT(seen, *result);
+ return true;
+ }
+
+ bool invoke(NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result)
+ {
+ if (identifierIs(methodName, "get"))
+ return get(args, argCount, result, false);
+
+ if (identifierIs(methodName, "getAsync"))
+ return get(args, argCount, result, true);
+
+ if (identifierIs(methodName, "serviceAsync"))
+ return serviceAsync(args, argCount, result);
+
+ return false;
+ }
+ };
+
+ virtual NPError NPP_GetValue(NPPVariable variable, void *value)
+ {
+ if (variable != NPPVpluginScriptableNPObject)
+ return NPERR_GENERIC_ERROR;
+
+ *(NPObject**)value = ScriptableObject::create(this);
+
+ return NPERR_NO_ERROR;
+ }
+
+ virtual bool NPP_URLNotify(const char* url, NPReason reason, void* notifyData)
+ {
+ NPVariant args[2];
+
+ NPObject* windowScriptObject;
+ NPN_GetValue(NPNVWindowNPObject, &windowScriptObject);
+
+ NPIdentifier callbackIdentifier = notifyData;
+
+ INT32_TO_NPVARIANT(reason, args[0]);
+ STRINGZ_TO_NPVARIANT(url, args[1]);
+
+ NPVariant browserResult;
+ if (NPN_Invoke(windowScriptObject, callbackIdentifier, args, 2, &browserResult))
+ NPN_ReleaseVariantValue(&browserResult);
+
+ return true;
+ }
+
+ virtual void NPP_URLRedirectNotify(const char*, int32_t, void* notifyData)
+ {
+ Redirect& redirect = redirects[notifyData];
+ if (redirect.async) {
+ redirect.hasFired = false;
+ return;
+ }
+
+ NPN_URLRedirectResponse(notifyData, redirect.redirectsRemaining);
+ if (redirect.redirectsRemaining)
+ --redirect.redirectsRemaining;
+ }
+};
+
+static PluginTest::Register<URLRedirect> urlRedirect("url-redirect");
+
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/x11/CallInvalidateRectWithNullNPPArgument.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/x11/CallInvalidateRectWithNullNPPArgument.cpp
new file mode 100644
index 000000000..8d25d3906
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/x11/CallInvalidateRectWithNullNPPArgument.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include "PluginObject.h"
+
+using namespace std;
+
+class CallInvalidateRectWithNullNPPArgument : public PluginTest {
+public:
+ CallInvalidateRectWithNullNPPArgument(NPP npp, const string& identifier)
+ : PluginTest(npp, identifier)
+ {
+ }
+
+private:
+ virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved)
+ {
+ NPRect rect;
+ browser->invalidaterect(0, &rect);
+ pluginLog(m_npp, "SUCCESS!");
+ return NPERR_NO_ERROR;
+ }
+
+};
+
+static PluginTest::Register<CallInvalidateRectWithNullNPPArgument> callInvalidateRectWithNullNPPArgument("call-invalidate-rect-with-null-npp-argument");
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/main.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/main.cpp
new file mode 100644
index 000000000..d7736bca1
--- /dev/null
+++ b/Tools/DumpRenderTree/TestNetscapePlugIn/main.cpp
@@ -0,0 +1,874 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+
+#include "PluginTest.h"
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
+#ifdef XP_UNIX
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#endif
+
+#if !defined(NP_NO_CARBON) && defined(QD_HEADERS_ARE_PRIVATE) && QD_HEADERS_ARE_PRIVATE
+extern "C" void GlobalToLocal(Point*);
+#endif
+
+using namespace std;
+
+#define CRASH() do { \
+ *(int *)(uintptr_t)0xbbadbeef = 0; \
+ ((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \
+} while(false)
+
+static bool getEntryPointsWasCalled;
+static bool initializeWasCalled;
+
+#if defined(XP_WIN)
+#define STDCALL __stdcall
+
+static inline int strcasecmp(const char* s1, const char* s2)
+{
+ return _stricmp(s1, s2);
+}
+
+#else
+#define STDCALL
+#endif
+
+extern "C" {
+NPError STDCALL NP_GetEntryPoints(NPPluginFuncs *pluginFuncs);
+}
+
+// Entry points
+extern "C"
+NPError STDCALL NP_Initialize(NPNetscapeFuncs *browserFuncs
+#ifdef XP_UNIX
+ , NPPluginFuncs *pluginFuncs
+#endif
+ )
+{
+ initializeWasCalled = true;
+
+#if defined(XP_WIN)
+ // Simulate Flash and QuickTime's behavior of crashing when NP_Initialize is called before NP_GetEntryPoints.
+ if (!getEntryPointsWasCalled)
+ CRASH();
+#endif
+
+ browser = browserFuncs;
+
+#ifdef XP_UNIX
+ return NP_GetEntryPoints(pluginFuncs);
+#else
+ return NPERR_NO_ERROR;
+#endif
+}
+
+extern "C"
+NPError STDCALL NP_GetEntryPoints(NPPluginFuncs *pluginFuncs)
+{
+ getEntryPointsWasCalled = true;
+
+#ifdef XP_MACOSX
+ // Simulate Silverlight's behavior of crashing when NP_GetEntryPoints is called before NP_Initialize.
+ if (!initializeWasCalled)
+ CRASH();
+#endif
+
+ pluginFunctions = pluginFuncs;
+
+ pluginFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+ pluginFuncs->size = sizeof(pluginFuncs);
+ pluginFuncs->newp = NPP_New;
+ pluginFuncs->destroy = NPP_Destroy;
+ pluginFuncs->setwindow = NPP_SetWindow;
+ pluginFuncs->newstream = NPP_NewStream;
+ pluginFuncs->destroystream = NPP_DestroyStream;
+ pluginFuncs->asfile = NPP_StreamAsFile;
+ pluginFuncs->writeready = NPP_WriteReady;
+ pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write;
+ pluginFuncs->print = NPP_Print;
+ pluginFuncs->event = NPP_HandleEvent;
+ pluginFuncs->urlnotify = NPP_URLNotify;
+ pluginFuncs->urlredirectnotify = NPP_URLRedirectNotify;
+ pluginFuncs->getvalue = NPP_GetValue;
+ pluginFuncs->setvalue = NPP_SetValue;
+
+ return NPERR_NO_ERROR;
+}
+
+extern "C"
+void STDCALL NP_Shutdown(void)
+{
+ PluginTest::NP_Shutdown();
+}
+
+static void executeScript(const PluginObject* obj, const char* script);
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved)
+{
+#ifdef XP_MACOSX
+ NPEventModel eventModel;
+
+ // Always turn on the CG model
+ NPBool supportsCoreGraphics;
+ if (browser->getvalue(instance, NPNVsupportsCoreGraphicsBool, &supportsCoreGraphics) != NPERR_NO_ERROR)
+ supportsCoreGraphics = false;
+
+ if (!supportsCoreGraphics)
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+ NPDrawingModel drawingModelToUse = NPDrawingModelCoreGraphics;
+
+ NPBool supportsCoreAnimation;
+ if (browser->getvalue(instance, NPNVsupportsCoreAnimationBool, &supportsCoreAnimation) != NPERR_NO_ERROR)
+ supportsCoreAnimation = false;
+
+#ifndef NP_NO_CARBON
+ NPBool supportsCarbon = false;
+#endif
+ NPBool supportsCocoa = false;
+
+#ifndef NP_NO_CARBON
+ // A browser that doesn't know about NPNVsupportsCarbonBool is one that only supports Carbon event model.
+ if (browser->getvalue(instance, NPNVsupportsCarbonBool, &supportsCarbon) != NPERR_NO_ERROR)
+ supportsCarbon = true;
+#endif
+
+ if (browser->getvalue(instance, NPNVsupportsCocoaBool, &supportsCocoa) != NPERR_NO_ERROR)
+ supportsCocoa = false;
+
+ if (supportsCocoa) {
+ eventModel = NPEventModelCocoa;
+#ifndef NP_NO_CARBON
+ } else if (supportsCarbon) {
+ eventModel = NPEventModelCarbon;
+#endif
+ } else {
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+ }
+
+ browser->setvalue(instance, NPPVpluginEventModel, (void *)eventModel);
+#endif // XP_MACOSX
+
+ PluginObject* obj = (PluginObject*)browser->createobject(instance, getPluginClass());
+ instance->pdata = obj;
+
+#ifdef XP_MACOSX
+ obj->eventModel = eventModel;
+ obj->coreAnimationLayer = 0;
+#endif // XP_MACOSX
+
+ string testIdentifier;
+ const char* onNewScript = 0;
+
+ for (int i = 0; i < argc; i++) {
+ if (strcasecmp(argn[i], "test") == 0)
+ testIdentifier = argv[i];
+ if (strcasecmp(argn[i], "onstreamload") == 0 && !obj->onStreamLoad)
+ obj->onStreamLoad = strdup(argv[i]);
+ else if (strcasecmp(argn[i], "onStreamDestroy") == 0 && !obj->onStreamDestroy)
+ obj->onStreamDestroy = strdup(argv[i]);
+ else if (strcasecmp(argn[i], "onURLNotify") == 0 && !obj->onURLNotify)
+ obj->onURLNotify = strdup(argv[i]);
+ else if (strcasecmp(argn[i], "src") == 0 &&
+ strcasecmp(argv[i], "data:application/x-webkit-test-netscape,returnerrorfromnewstream") == 0)
+ obj->returnErrorFromNewStream = TRUE;
+ else if (strcasecmp(argn[i], "src") == 0 &&
+ strcasecmp(argv[i], "data:application/x-webkit-test-netscape,alertwhenloaded") == 0)
+ executeScript(obj, "alert('Plugin Loaded!')");
+ else if (strcasecmp(argn[i], "src") == 0 &&
+ strcasecmp(argv[i], "data:application/x-webkit-test-netscape,logifloaded") == 0) {
+ for (int j = 0; j < argc; j++) {
+ if (strcasecmp(argn[j], "log") == 0) {
+ int length = 26 + strlen(argv[j]) + 1;
+ char* buffer = (char*) malloc(length);
+ snprintf(buffer, length, "xWebkitTestNetscapeLog('%s')", argv[j]);
+ executeScript(obj, buffer);
+ free(buffer);
+ }
+ }
+ } else if (strcasecmp(argn[i], "onSetWindow") == 0 && !obj->onSetWindow)
+ obj->onSetWindow = strdup(argv[i]);
+ else if (strcasecmp(argn[i], "onNew") == 0 && !onNewScript)
+ onNewScript = argv[i];
+ else if (strcasecmp(argn[i], "onPaintEvent") == 0 && !obj->onPaintEvent)
+ obj->onPaintEvent = strdup(argv[i]);
+ else if (strcasecmp(argn[i], "logfirstsetwindow") == 0)
+ obj->logSetWindow = TRUE;
+ else if (strcasecmp(argn[i], "testnpruntime") == 0)
+ testNPRuntime(instance);
+ else if (strcasecmp(argn[i], "logSrc") == 0) {
+ for (int i = 0; i < argc; i++)
+ if (strcasecmp(argn[i], "src") == 0)
+ pluginLog(instance, "src: %s", argv[i]);
+ } else if (strcasecmp(argn[i], "cleardocumentduringnew") == 0)
+ executeScript(obj, "document.body.innerHTML = ''");
+ else if (!strcasecmp(argn[i], "ondestroy"))
+ obj->onDestroy = strdup(argv[i]);
+ else if (strcasecmp(argn[i], "testwindowopen") == 0)
+ obj->testWindowOpen = TRUE;
+ else if (strcasecmp(argn[i], "drawingmodel") == 0) {
+#ifdef XP_MACOSX
+ const char* value = argv[i];
+ if (strcasecmp(value, "coreanimation") == 0) {
+ if (supportsCoreAnimation)
+ drawingModelToUse = NPDrawingModelCoreAnimation;
+ else
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+ } else if (strcasecmp(value, "coregraphics") == 0) {
+ if (supportsCoreGraphics)
+ drawingModelToUse = NPDrawingModelCoreGraphics;
+ else
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+ } else
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+#endif
+ } else if (strcasecmp(argn[i], "testGetURLOnDestroy") == 0) {
+#if defined(XP_WIN)
+ // FIXME: When https://bugs.webkit.org/show_bug.cgi?id=41831 is fixed, this #ifdef can be removed.
+ obj->testGetURLOnDestroy = TRUE;
+#endif
+ } else if (!strcasecmp(argn[i], "src") && strstr(argv[i], "plugin-document-has-focus.pl"))
+ obj->testKeyboardFocusForPlugins = TRUE;
+ else if (!strcasecmp(argn[i], "evaluatescript")) {
+ char* script = argv[i];
+ if (script == strstr(script, "mouse::")) {
+ obj->mouseDownForEvaluateScript = true;
+ obj->evaluateScriptOnMouseDownOrKeyDown = strdup(script + sizeof("mouse::") - 1);
+ } else if (script == strstr(script, "key::")) {
+ obj->evaluateScriptOnMouseDownOrKeyDown = strdup(script + sizeof("key::") - 1);
+ }
+ // When testing evaluate script on mouse-down or key-down, allow event logging to handle events.
+ if (obj->evaluateScriptOnMouseDownOrKeyDown)
+ obj->eventLogging = true;
+ } else if (!strcasecmp(argn[i], "windowedPlugin")) {
+ void* windowed = 0;
+ if (!strcasecmp(argv[i], "false") || !strcasecmp(argv[i], "0"))
+ windowed = 0;
+ else if (!strcasecmp(argv[i], "true") || !strcasecmp(argv[i], "1"))
+ windowed = reinterpret_cast<void*>(1);
+ else
+ assert(false);
+ browser->setvalue(instance, NPPVpluginWindowBool, windowed);
+ }
+ }
+
+#ifdef XP_MACOSX
+ browser->setvalue(instance, NPPVpluginDrawingModel, (void *)drawingModelToUse);
+ if (drawingModelToUse == NPDrawingModelCoreAnimation)
+ obj->coreAnimationLayer = createCoreAnimationLayer();
+#endif
+
+ obj->pluginTest = PluginTest::create(instance, testIdentifier);
+
+ if (!obj->pluginTest) {
+ pluginLog(instance, "NPP_New: Could not find a test named \"%s\", maybe its .cpp file wasn't added to the build system?", testIdentifier.c_str());
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (onNewScript)
+ executeScript(obj, onNewScript);
+
+ return obj->pluginTest->NPP_New(pluginType, mode, argc, argn, argv, saved);
+}
+
+NPError NPP_Destroy(NPP instance, NPSavedData **save)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+ if (obj) {
+ if (obj->testGetURLOnDestroy)
+ browser->geturlnotify(obj->npp, "about:blank", "", 0);
+
+ if (obj->onDestroy) {
+ executeScript(obj, obj->onDestroy);
+ free(obj->onDestroy);
+ }
+
+ if (obj->onStreamLoad)
+ free(obj->onStreamLoad);
+
+ if (obj->onStreamDestroy)
+ free(obj->onStreamDestroy);
+
+ if (obj->onURLNotify)
+ free(obj->onURLNotify);
+
+ if (obj->onSetWindow)
+ free(obj->onSetWindow);
+
+ if (obj->onPaintEvent)
+ free(obj->onPaintEvent);
+
+ if (obj->evaluateScriptOnMouseDownOrKeyDown)
+ free(obj->evaluateScriptOnMouseDownOrKeyDown);
+
+ if (obj->logDestroy)
+ pluginLog(instance, "NPP_Destroy");
+
+#ifdef XP_MACOSX
+ if (obj->coreAnimationLayer)
+ CFRelease(obj->coreAnimationLayer);
+#endif
+
+ if (obj->pluginTest)
+ obj->pluginTest->NPP_Destroy(save);
+
+ browser->releaseobject(&obj->header);
+ }
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_SetWindow(NPP instance, NPWindow *window)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+
+ if (obj) {
+ obj->lastWindow = *window;
+
+ if (obj->logSetWindow) {
+ pluginLog(instance, "NPP_SetWindow: %d %d", (int)window->width, (int)window->height);
+ obj->logSetWindow = FALSE;
+ executeScript(obj, "testRunner.notifyDone();");
+ }
+
+ if (obj->onSetWindow)
+ executeScript(obj, obj->onSetWindow);
+
+ if (obj->testWindowOpen) {
+ testWindowOpen(instance);
+ obj->testWindowOpen = FALSE;
+ }
+
+ if (obj->testKeyboardFocusForPlugins) {
+ obj->eventLogging = true;
+ executeScript(obj, "eventSender.keyDown('A');");
+ }
+ }
+
+ return obj->pluginTest->NPP_SetWindow(window);
+}
+
+static void executeScript(const PluginObject* obj, const char* script)
+{
+ NPObject *windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPString npScript;
+ npScript.UTF8Characters = script;
+ npScript.UTF8Length = strlen(script);
+
+ NPVariant browserResult;
+ browser->evaluate(obj->npp, windowScriptObject, &npScript, &browserResult);
+ browser->releasevariantvalue(&browserResult);
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream, NPBool seekable, uint16_t *stype)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+ obj->stream = stream;
+ *stype = NP_NORMAL;
+
+ if (obj->returnErrorFromNewStream)
+ return NPERR_GENERIC_ERROR;
+
+ if (browser->version >= NPVERS_HAS_RESPONSE_HEADERS)
+ notifyStream(obj, stream->url, stream->headers);
+
+ if (obj->onStreamLoad)
+ executeScript(obj, obj->onStreamLoad);
+
+ return obj->pluginTest->NPP_NewStream(type, stream, seekable, stype);
+}
+
+NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason)
+{
+ PluginObject* obj = (PluginObject*)instance->pdata;
+
+ if (obj->onStreamDestroy) {
+ NPObject* windowObject = 0;
+ NPError error = browser->getvalue(instance, NPNVWindowNPObject, &windowObject);
+
+ if (error == NPERR_NO_ERROR) {
+ NPVariant onStreamDestroyVariant;
+ if (browser->getproperty(instance, windowObject, browser->getstringidentifier(obj->onStreamDestroy), &onStreamDestroyVariant)) {
+ if (NPVARIANT_IS_OBJECT(onStreamDestroyVariant)) {
+ NPObject* onStreamDestroyFunction = NPVARIANT_TO_OBJECT(onStreamDestroyVariant);
+
+ NPVariant reasonVariant;
+ INT32_TO_NPVARIANT(reason, reasonVariant);
+
+ NPVariant result;
+ browser->invokeDefault(instance, onStreamDestroyFunction, &reasonVariant, 1, &result);
+ browser->releasevariantvalue(&result);
+ }
+ browser->releasevariantvalue(&onStreamDestroyVariant);
+ }
+ browser->releaseobject(windowObject);
+ }
+ }
+
+ return obj->pluginTest->NPP_DestroyStream(stream, reason);
+}
+
+int32_t NPP_WriteReady(NPP instance, NPStream *stream)
+{
+ PluginObject* obj = (PluginObject*)instance->pdata;
+ return obj->pluginTest->NPP_WriteReady(stream);
+}
+
+int32_t NPP_Write(NPP instance, NPStream *stream, int32_t offset, int32_t len, void *buffer)
+{
+ PluginObject* obj = (PluginObject*)instance->pdata;
+
+ if (obj->returnNegativeOneFromWrite)
+ return -1;
+
+ return obj->pluginTest->NPP_Write(stream, offset, len, buffer);
+}
+
+void NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname)
+{
+}
+
+void NPP_Print(NPP instance, NPPrint *platformPrint)
+{
+}
+
+#ifdef XP_MACOSX
+#ifndef NP_NO_CARBON
+static int16_t handleEventCarbon(NPP instance, PluginObject* obj, EventRecord* event)
+{
+ Point pt = { event->where.v, event->where.h };
+
+ switch (event->what) {
+ case nullEvent:
+ // these are delivered non-deterministically, don't log.
+ break;
+ case mouseDown:
+ if (obj->eventLogging) {
+#if __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ GlobalToLocal(&pt);
+#if __clang__
+#pragma clang diagnostic pop
+#endif
+ pluginLog(instance, "mouseDown at (%d, %d)", pt.h, pt.v);
+ }
+ if (obj->evaluateScriptOnMouseDownOrKeyDown && obj->mouseDownForEvaluateScript)
+ executeScript(obj, obj->evaluateScriptOnMouseDownOrKeyDown);
+ break;
+ case mouseUp:
+ if (obj->eventLogging) {
+#if __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ GlobalToLocal(&pt);
+#if __clang__
+#pragma clang diagnostic pop
+#endif
+ pluginLog(instance, "mouseUp at (%d, %d)", pt.h, pt.v);
+ }
+ break;
+ case keyDown:
+ if (obj->eventLogging)
+ pluginLog(instance, "keyDown '%c'", (char)(event->message & 0xFF));
+ if (obj->evaluateScriptOnMouseDownOrKeyDown && !obj->mouseDownForEvaluateScript)
+ executeScript(obj, obj->evaluateScriptOnMouseDownOrKeyDown);
+ break;
+ case keyUp:
+ if (obj->eventLogging)
+ pluginLog(instance, "keyUp '%c'", (char)(event->message & 0xFF));
+ if (obj->testKeyboardFocusForPlugins) {
+ obj->eventLogging = false;
+ obj->testKeyboardFocusForPlugins = FALSE;
+ executeScript(obj, "testRunner.notifyDone();");
+ }
+ break;
+ case autoKey:
+ if (obj->eventLogging)
+ pluginLog(instance, "autoKey '%c'", (char)(event->message & 0xFF));
+ break;
+ case updateEvt:
+ if (obj->eventLogging)
+ pluginLog(instance, "updateEvt");
+ break;
+ case diskEvt:
+ if (obj->eventLogging)
+ pluginLog(instance, "diskEvt");
+ break;
+ case activateEvt:
+ if (obj->eventLogging)
+ pluginLog(instance, "activateEvt");
+ break;
+ case osEvt:
+ if (!obj->eventLogging)
+ break;
+ printf("PLUGIN: osEvt - ");
+ switch ((event->message & 0xFF000000) >> 24) {
+ case suspendResumeMessage:
+ printf("%s\n", (event->message & 0x1) ? "resume" : "suspend");
+ break;
+ case mouseMovedMessage:
+ printf("mouseMoved\n");
+ break;
+ default:
+ printf("%08lX\n", event->message);
+ }
+ break;
+ case kHighLevelEvent:
+ if (obj->eventLogging)
+ pluginLog(instance, "kHighLevelEvent");
+ break;
+ // NPAPI events
+ case NPEventType_GetFocusEvent:
+ if (obj->eventLogging)
+ pluginLog(instance, "getFocusEvent");
+ break;
+ case NPEventType_LoseFocusEvent:
+ if (obj->eventLogging)
+ pluginLog(instance, "loseFocusEvent");
+ break;
+ case NPEventType_AdjustCursorEvent:
+ if (obj->eventLogging)
+ pluginLog(instance, "adjustCursorEvent");
+ break;
+ default:
+ if (obj->eventLogging)
+ pluginLog(instance, "event %d", event->what);
+ }
+
+ return 0;
+}
+#endif
+
+static int16_t handleEventCocoa(NPP instance, PluginObject* obj, NPCocoaEvent* event)
+{
+ switch (event->type) {
+ case NPCocoaEventWindowFocusChanged:
+
+ case NPCocoaEventFocusChanged:
+ if (obj->eventLogging) {
+ if (event->data.focus.hasFocus)
+ pluginLog(instance, "getFocusEvent");
+ else
+ pluginLog(instance, "loseFocusEvent");
+ }
+ return 1;
+
+ case NPCocoaEventDrawRect: {
+ if (obj->onPaintEvent)
+ executeScript(obj, obj->onPaintEvent);
+ return 1;
+ }
+
+ case NPCocoaEventKeyDown:
+ if (obj->eventLogging && event->data.key.characters)
+ pluginLog(instance, "keyDown '%c'", CFStringGetCharacterAtIndex(reinterpret_cast<CFStringRef>(event->data.key.characters), 0));
+ if (obj->evaluateScriptOnMouseDownOrKeyDown && !obj->mouseDownForEvaluateScript)
+ executeScript(obj, obj->evaluateScriptOnMouseDownOrKeyDown);
+ return 1;
+
+ case NPCocoaEventKeyUp:
+ if (obj->eventLogging && event->data.key.characters) {
+ pluginLog(instance, "keyUp '%c'", CFStringGetCharacterAtIndex(reinterpret_cast<CFStringRef>(event->data.key.characters), 0));
+ if (obj->testKeyboardFocusForPlugins) {
+ obj->eventLogging = false;
+ obj->testKeyboardFocusForPlugins = FALSE;
+ executeScript(obj, "testRunner.notifyDone();");
+ }
+ }
+ return 1;
+
+ case NPCocoaEventFlagsChanged:
+ return 1;
+
+ case NPCocoaEventMouseDown:
+ if (obj->eventLogging) {
+ pluginLog(instance, "mouseDown at (%d, %d)",
+ (int)event->data.mouse.pluginX,
+ (int)event->data.mouse.pluginY);
+ }
+ if (obj->evaluateScriptOnMouseDownOrKeyDown && obj->mouseDownForEvaluateScript)
+ executeScript(obj, obj->evaluateScriptOnMouseDownOrKeyDown);
+ return 1;
+ case NPCocoaEventMouseUp:
+ if (obj->eventLogging) {
+ pluginLog(instance, "mouseUp at (%d, %d)",
+ (int)event->data.mouse.pluginX,
+ (int)event->data.mouse.pluginY);
+ }
+ return 1;
+
+ case NPCocoaEventMouseMoved:
+ case NPCocoaEventMouseEntered:
+ case NPCocoaEventMouseExited:
+ case NPCocoaEventMouseDragged:
+ case NPCocoaEventScrollWheel:
+ case NPCocoaEventTextInput:
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif // XP_MACOSX
+
+#ifdef XP_UNIX
+
+static char keyEventToChar(XKeyEvent* event)
+{
+ char c = ' ';
+ XLookupString(event, &c, sizeof(c), 0, 0);
+ return c;
+}
+
+static int16_t handleEventX11(NPP instance, PluginObject* obj, XEvent* event)
+{
+ switch (event->type) {
+ case ButtonPress:
+ if (obj->eventLogging)
+ pluginLog(instance, "mouseDown at (%d, %d)", event->xbutton.x, event->xbutton.y);
+ if (obj->evaluateScriptOnMouseDownOrKeyDown && obj->mouseDownForEvaluateScript)
+ executeScript(obj, obj->evaluateScriptOnMouseDownOrKeyDown);
+ break;
+ case ButtonRelease:
+ if (obj->eventLogging)
+ pluginLog(instance, "mouseUp at (%d, %d)", event->xbutton.x, event->xbutton.y);
+ break;
+ case KeyPress:
+ // FIXME: extract key code
+ if (obj->eventLogging)
+ pluginLog(instance, "keyDown '%c'", keyEventToChar(&event->xkey));
+ if (obj->evaluateScriptOnMouseDownOrKeyDown && !obj->mouseDownForEvaluateScript)
+ executeScript(obj, obj->evaluateScriptOnMouseDownOrKeyDown);
+ break;
+ case KeyRelease:
+ // FIXME: extract key code
+ if (obj->eventLogging)
+ pluginLog(instance, "keyUp '%c'", keyEventToChar(&event->xkey));
+ if (obj->testKeyboardFocusForPlugins) {
+ obj->eventLogging = false;
+ obj->testKeyboardFocusForPlugins = FALSE;
+ executeScript(obj, "testRunner.notifyDone();");
+ }
+ break;
+ case GraphicsExpose:
+ if (obj->eventLogging)
+ pluginLog(instance, "updateEvt");
+ if (obj->onPaintEvent)
+ executeScript(obj, obj->onPaintEvent);
+ break;
+ // NPAPI events
+ case FocusIn:
+ if (obj->eventLogging)
+ pluginLog(instance, "getFocusEvent");
+ break;
+ case FocusOut:
+ if (obj->eventLogging)
+ pluginLog(instance, "loseFocusEvent");
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ case MotionNotify:
+ break;
+ default:
+ if (obj->eventLogging)
+ pluginLog(instance, "event %d", event->type);
+ }
+
+ fflush(stdout);
+ return 0;
+}
+#endif // XP_UNIX
+
+#ifdef XP_WIN
+static int16_t handleEventWin(NPP instance, PluginObject* obj, NPEvent* event)
+{
+ switch (event->event) {
+ case WM_PAINT:
+ if (obj->onPaintEvent)
+ executeScript(obj, obj->onPaintEvent);
+ break;
+ case WM_KEYDOWN:
+ if (obj->eventLogging)
+ pluginLog(instance, "keyDown '%c'", event->wParam);
+ if (obj->evaluateScriptOnMouseDownOrKeyDown && !obj->mouseDownForEvaluateScript)
+ executeScript(obj, obj->evaluateScriptOnMouseDownOrKeyDown);
+ break;
+ case WM_CHAR:
+ break;
+ case WM_KEYUP:
+ if (obj->eventLogging)
+ pluginLog(instance, "keyUp '%c'", event->wParam);
+ if (obj->testKeyboardFocusForPlugins) {
+ obj->eventLogging = false;
+ obj->testKeyboardFocusForPlugins = FALSE;
+ executeScript(obj, "testRunner.notifyDone();");
+ }
+ break;
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ if (obj->eventLogging)
+ pluginLog(instance, "mouseDown at (%d, %d)", LOWORD(event->lParam), HIWORD(event->lParam));
+ if (obj->evaluateScriptOnMouseDownOrKeyDown && obj->mouseDownForEvaluateScript)
+ executeScript(obj, obj->evaluateScriptOnMouseDownOrKeyDown);
+ break;
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ if (obj->eventLogging)
+ pluginLog(instance, "mouseUp at (%d, %d)", LOWORD(event->lParam), HIWORD(event->lParam));
+ break;
+ case WM_SETFOCUS:
+ if (obj->eventLogging)
+ pluginLog(instance, "getFocusEvent");
+ break;
+ case WM_KILLFOCUS:
+ if (obj->eventLogging)
+ pluginLog(instance, "loseFocusEvent");
+ break;
+ }
+ return 0;
+}
+#endif // XP_WIN
+
+int16_t NPP_HandleEvent(NPP instance, void *event)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+
+ if (obj->pluginTest->NPP_HandleEvent(event) == 1)
+ return 1;
+
+#ifdef XP_MACOSX
+#ifndef NP_NO_CARBON
+ if (obj->eventModel == NPEventModelCarbon)
+ return handleEventCarbon(instance, obj, static_cast<EventRecord*>(event));
+#endif
+
+ assert(obj->eventModel == NPEventModelCocoa);
+ return handleEventCocoa(instance, obj, static_cast<NPCocoaEvent*>(event));
+#elif defined(XP_UNIX)
+ return handleEventX11(instance, obj, static_cast<XEvent*>(event));
+#elif defined(XP_WIN)
+ return handleEventWin(instance, obj, static_cast<NPEvent*>(event));
+#else
+ // FIXME: Implement for other platforms.
+ return 0;
+#endif // XP_MACOSX
+}
+
+void NPP_URLNotify(NPP instance, const char *url, NPReason reason, void *notifyData)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+ if (obj->pluginTest->NPP_URLNotify(url, reason, notifyData))
+ return;
+
+ if (obj->onURLNotify)
+ executeScript(obj, obj->onURLNotify);
+
+ handleCallback(obj, url, reason, notifyData);
+}
+
+void NPP_URLRedirectNotify(NPP instance, const char *url, int32_t status, void *notifyData)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+ obj->pluginTest->NPP_URLRedirectNotify(url, status, notifyData);
+}
+
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
+{
+#ifdef XP_UNIX
+ if (variable == NPPVpluginNameString) {
+ *((char **)value) = const_cast<char*>("WebKit Test PlugIn");
+ return NPERR_NO_ERROR;
+ }
+ if (variable == NPPVpluginDescriptionString) {
+ *((char **)value) = const_cast<char*>("Simple Netscape® plug-in that handles test content for WebKit");
+ return NPERR_NO_ERROR;
+ }
+ if (variable == NPPVpluginNeedsXEmbed) {
+ *((NPBool *)value) = TRUE;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ if (!instance)
+ return NPERR_GENERIC_ERROR;
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+
+ // First, check if the PluginTest object supports getting this value.
+ if (obj->pluginTest->NPP_GetValue(variable, value) == NPERR_NO_ERROR)
+ return NPERR_NO_ERROR;
+
+ if (variable == NPPVpluginScriptableNPObject) {
+ void **v = (void **)value;
+ // Return value is expected to be retained
+ browser->retainobject((NPObject *)obj);
+ *v = obj;
+ return NPERR_NO_ERROR;
+ }
+
+#ifdef XP_MACOSX
+ if (variable == NPPVpluginCoreAnimationLayer) {
+ if (!obj->coreAnimationLayer)
+ return NPERR_GENERIC_ERROR;
+
+ void **v = (void **)value;
+ *v = (void*)CFRetain(obj->coreAnimationLayer);
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+ return obj->pluginTest->NPP_SetValue(variable, value);
+}
+
+#ifdef XP_UNIX
+extern "C"
+const char* NP_GetMIMEDescription(void)
+{
+ return "application/x-webkit-test-netscape:testnetscape:test netscape content;image/png:png:PNG image";
+}
+
+extern "C"
+NPError NP_GetValue(NPP instance, NPPVariable variable, void* value)
+{
+ return NPP_GetValue(instance, variable, value);
+}
+#endif
diff --git a/Tools/DumpRenderTree/TestRunner.cpp b/Tools/DumpRenderTree/TestRunner.cpp
new file mode 100644
index 000000000..95140d26a
--- /dev/null
+++ b/Tools/DumpRenderTree/TestRunner.cpp
@@ -0,0 +1,2225 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Joone Hur <joone@kldp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestRunner.h"
+
+#include "WorkQueue.h"
+#include "WorkQueueItem.h"
+#include <JavaScriptCore/APICast.h>
+#include <JavaScriptCore/JSContextRef.h>
+#include <JavaScriptCore/JSCTestRunnerUtils.h>
+#include <JavaScriptCore/JSObjectRef.h>
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <cstring>
+#include <locale.h>
+#include <runtime/ArrayBufferView.h>
+#include <runtime/JSArrayBufferView.h>
+#include <runtime/TypedArrayInlines.h>
+#include <stdio.h>
+#include <wtf/Assertions.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/MathExtras.h>
+#include <wtf/RefPtr.h>
+#include <wtf/StdLibExtras.h>
+
+#if PLATFORM(MAC) && !PLATFORM(IOS)
+#include <Carbon/Carbon.h>
+#endif
+
+const unsigned TestRunner::viewWidth = 800;
+const unsigned TestRunner::viewHeight = 600;
+
+const unsigned TestRunner::w3cSVGViewWidth = 480;
+const unsigned TestRunner::w3cSVGViewHeight = 360;
+
+TestRunner::TestRunner(const std::string& testURL, const std::string& expectedPixelHash)
+ : m_disallowIncreaseForApplicationCacheQuota(false)
+ , m_dumpApplicationCacheDelegateCallbacks(false)
+ , m_dumpAsAudio(false)
+ , m_dumpAsPDF(false)
+ , m_dumpAsText(false)
+ , m_dumpBackForwardList(false)
+ , m_dumpChildFrameScrollPositions(false)
+ , m_dumpChildFramesAsText(false)
+ , m_dumpDOMAsWebArchive(false)
+ , m_dumpDatabaseCallbacks(false)
+ , m_dumpEditingCallbacks(false)
+ , m_dumpFrameLoadCallbacks(false)
+ , m_dumpProgressFinishedCallback(false)
+ , m_dumpUserGestureInFrameLoadCallbacks(false)
+ , m_dumpHistoryDelegateCallbacks(false)
+ , m_dumpResourceLoadCallbacks(false)
+ , m_dumpResourceResponseMIMETypes(false)
+ , m_dumpSelectionRect(false)
+ , m_dumpSourceAsWebArchive(false)
+ , m_dumpStatusCallbacks(false)
+ , m_dumpTitleChanges(false)
+ , m_dumpIconChanges(false)
+ , m_dumpVisitedLinksCallback(false)
+ , m_dumpWillCacheResponse(false)
+ , m_generatePixelResults(true)
+ , m_callCloseOnWebViews(true)
+ , m_canOpenWindows(false)
+ , m_closeRemainingWindowsWhenComplete(true)
+ , m_newWindowsCopyBackForwardList(false)
+ , m_stopProvisionalFrameLoads(false)
+ , m_testOnscreen(false)
+ , m_testRepaint(false)
+ , m_testRepaintSweepHorizontally(false)
+ , m_waitToDump(false)
+ , m_willSendRequestReturnsNull(false)
+ , m_willSendRequestReturnsNullOnRedirect(false)
+ , m_windowIsKey(true)
+ , m_alwaysAcceptCookies(false)
+ , m_globalFlag(false)
+ , m_isGeolocationPermissionSet(false)
+ , m_geolocationPermission(false)
+ , m_handlesAuthenticationChallenges(false)
+ , m_isPrinting(false)
+ , m_deferMainResourceDataLoad(true)
+ , m_useDeferredFrameLoading(false)
+ , m_shouldPaintBrokenImage(true)
+ , m_shouldStayOnPageAfterHandlingBeforeUnload(false)
+ , m_areLegacyWebNotificationPermissionRequestsIgnored(false)
+ , m_customFullScreenBehavior(false)
+ , m_hasPendingWebNotificationClick(false)
+ , m_databaseDefaultQuota(-1)
+ , m_databaseMaxQuota(-1)
+ , m_testURL(testURL)
+ , m_expectedPixelHash(expectedPixelHash)
+ , m_titleTextDirection("ltr")
+ , m_timeout(0)
+{
+}
+
+PassRefPtr<TestRunner> TestRunner::create(const std::string& testURL, const std::string& expectedPixelHash)
+{
+ return adoptRef(new TestRunner(testURL, expectedPixelHash));
+}
+
+// Static Functions
+
+static JSValueRef disallowIncreaseForApplicationCacheQuotaCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDisallowIncreaseForApplicationCacheQuota(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpApplicationCacheDelegateCallbacksCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpApplicationCacheDelegateCallbacks(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpAsPDFCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpAsPDF(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpAsTextCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpAsText(true);
+
+ // Optional paramater, describing whether it's allowed to dump pixel results in dumpAsText mode.
+ controller->setGeneratePixelResults(argumentCount > 0 ? JSValueToBoolean(context, arguments[0]) : false);
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpBackForwardListCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpBackForwardList(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpChildFramesAsTextCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpChildFramesAsText(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpChildFrameScrollPositionsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpChildFrameScrollPositions(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpDatabaseCallbacksCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpDatabaseCallbacks(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpDOMAsWebArchiveCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpDOMAsWebArchive(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpEditingCallbacksCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpEditingCallbacks(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpFrameLoadCallbacksCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpFrameLoadCallbacks(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpProgressFinishedCallbackCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpProgressFinishedCallback(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpUserGestureInFrameLoadCallbacksCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpUserGestureInFrameLoadCallbacks(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpResourceLoadCallbacksCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpResourceLoadCallbacks(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpResourceResponseMIMETypesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpResourceResponseMIMETypes(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpSelectionRectCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpSelectionRect(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpSourceAsWebArchiveCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpSourceAsWebArchive(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpStatusCallbacksCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpStatusCallbacks(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpTitleChangesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpTitleChanges(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpIconChangesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpIconChanges(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef dumpWillCacheResponseCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpWillCacheResponse(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef pathToLocalResourceCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ JSRetainPtr<JSStringRef> localPath(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ JSRetainPtr<JSStringRef> convertedPath(Adopt, controller->pathToLocalResource(context, localPath.get()));
+ if (!convertedPath)
+ return JSValueMakeUndefined(context);
+
+ return JSValueMakeString(context, convertedPath.get());
+}
+
+static JSValueRef removeAllVisitedLinksCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDumpVisitedLinksCallback(true);
+ controller->removeAllVisitedLinks();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef repaintSweepHorizontallyCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setTestRepaintSweepHorizontally(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setCallCloseOnWebViewsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setCallCloseOnWebViews(JSValueToBoolean(context, arguments[0]));
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setCanOpenWindowsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setCanOpenWindows(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setCloseRemainingWindowsWhenCompleteCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setCloseRemainingWindowsWhenComplete(JSValueToBoolean(context, arguments[0]));
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setAudioResultCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ // FIXME (123058): Use a JSC API to get buffer contents once such is exposed.
+ JSC::JSArrayBufferView* jsBufferView = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(toJS(toJS(context), arguments[0]));
+ ASSERT(jsBufferView);
+ RefPtr<JSC::ArrayBufferView> bufferView = jsBufferView->impl();
+ const char* buffer = static_cast<const char*>(bufferView->baseAddress());
+ std::vector<char> audioData(buffer, buffer + bufferView->byteLength());
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setAudioResult(audioData);
+ controller->setDumpAsAudio(true);
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef testOnscreenCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setTestOnscreen(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef testRepaintCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setTestRepaint(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef addDisallowedURLCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> url(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->addDisallowedURL(url.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef addURLToRedirectCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 2)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> origin(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ JSRetainPtr<JSStringRef> destination(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+
+ size_t maxLength = JSStringGetMaximumUTF8CStringSize(origin.get());
+ auto originBuffer = std::make_unique<char[]>(maxLength + 1);
+ JSStringGetUTF8CString(origin.get(), originBuffer.get(), maxLength + 1);
+
+ maxLength = JSStringGetMaximumUTF8CStringSize(destination.get());
+ auto destinationBuffer = std::make_unique<char[]>(maxLength + 1);
+ JSStringGetUTF8CString(destination.get(), destinationBuffer.get(), maxLength + 1);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->addURLToRedirect(originBuffer.get(), destinationBuffer.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef callShouldCloseOnWebViewCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ return JSValueMakeBoolean(context, controller->callShouldCloseOnWebView());
+}
+
+static JSValueRef clearAllApplicationCachesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->clearAllApplicationCaches();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef clearApplicationCacheForOriginCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> originURL(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->clearApplicationCacheForOrigin(originURL.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef applicationCacheDiskUsageForOriginCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> originURL(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ return JSValueMakeNumber(context, controller->applicationCacheDiskUsageForOrigin(originURL.get()));
+}
+
+static JSValueRef originsWithApplicationCacheCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ return controller->originsWithApplicationCache(context);
+}
+
+static JSValueRef clearAllDatabasesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->clearAllDatabases();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef clearBackForwardListCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->clearBackForwardList();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef clearPersistentUserStyleSheetCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->clearPersistentUserStyleSheet();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef decodeHostNameCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> name(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ JSRetainPtr<JSStringRef> decodedHostName(Adopt, controller->copyDecodedHostName(name.get()));
+ return JSValueMakeString(context, decodedHostName.get());
+}
+
+static JSValueRef dispatchPendingLoadRequestsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation, needs windows implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->dispatchPendingLoadRequests();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef displayCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->display();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef displayInvalidatedRegionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ // TestRunner::display() only renders the invalidated region so
+ // we can just use that.
+ controller->display();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef encodeHostNameCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> name(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ JSRetainPtr<JSStringRef> encodedHostName(Adopt, controller->copyEncodedHostName(name.get()));
+ return JSValueMakeString(context, encodedHostName.get());
+}
+
+static JSValueRef execCommandCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Mac & Windows implementations.
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> name(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ // Ignoring the second parameter (userInterface), as this command emulates a manual action.
+
+ JSRetainPtr<JSStringRef> value;
+ if (argumentCount >= 3) {
+ value.adopt(JSValueToStringCopy(context, arguments[2], exception));
+ ASSERT(!*exception);
+ } else
+ value.adopt(JSStringCreateWithUTF8CString(""));
+
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->execCommand(name.get(), value.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef findStringCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Mac implementation.
+ if (argumentCount < 2)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> target(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ JSObjectRef options = JSValueToObject(context, arguments[1], exception);
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ return JSValueMakeBoolean(context, controller->findString(context, target.get(), options));
+}
+
+static JSValueRef goBackCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->goBack();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef isCommandEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Mac implementation.
+
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> name(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ return JSValueMakeBoolean(context, controller->isCommandEnabled(name.get()));
+}
+
+static JSValueRef overridePreferenceCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 2)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> key(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> value(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->overridePreference(key.get(), value.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef keepWebHistoryCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->keepWebHistory();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef notifyDoneCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ // May be able to be made platform independant by using shared WorkQueue
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->notifyDone();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef numberOfPendingGeolocationPermissionRequestsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ return JSValueMakeNumber(context, controller->numberOfPendingGeolocationPermissionRequests());
+}
+
+static JSValueRef isGeolocationProviderActiveCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ return JSValueMakeBoolean(context, controller->isGeolocationProviderActive());
+}
+
+static JSValueRef queueBackNavigationCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ // May be able to be made platform independant by using shared WorkQueue
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ double howFarBackDouble = JSValueToNumber(context, arguments[0], exception);
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->queueBackNavigation(static_cast<int>(howFarBackDouble));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef queueForwardNavigationCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ // May be able to be made platform independant by using shared WorkQueue
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ double howFarForwardDouble = JSValueToNumber(context, arguments[0], exception);
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->queueForwardNavigation(static_cast<int>(howFarForwardDouble));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef queueLoadCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ // May be able to be made platform independant by using shared WorkQueue
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> url(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ JSRetainPtr<JSStringRef> target;
+ if (argumentCount >= 2) {
+ target.adopt(JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+ } else
+ target.adopt(JSStringCreateWithUTF8CString(""));
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->queueLoad(url.get(), target.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef queueLoadHTMLStringCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Mac & Windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> content(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ JSRetainPtr<JSStringRef> baseURL;
+ if (argumentCount >= 2) {
+ baseURL.adopt(JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+ } else
+ baseURL.adopt(JSStringCreateWithUTF8CString(""));
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ if (argumentCount >= 3) {
+ JSRetainPtr<JSStringRef> unreachableURL;
+ unreachableURL.adopt(JSValueToStringCopy(context, arguments[2], exception));
+ ASSERT(!*exception);
+ controller->queueLoadAlternateHTMLString(content.get(), baseURL.get(), unreachableURL.get());
+ return JSValueMakeUndefined(context);
+ }
+
+ controller->queueLoadHTMLString(content.get(), baseURL.get());
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef queueReloadCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ // May be able to be made platform independant by using shared WorkQueue
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->queueReload();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef queueLoadingScriptCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ // May be able to be made platform independant by using shared WorkQueue
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> script(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->queueLoadingScript(script.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef queueNonLoadingScriptCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ // May be able to be made platform independant by using shared WorkQueue
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> script(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->queueNonLoadingScript(script.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setAcceptsEditingCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setAcceptsEditing(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setAlwaysAcceptCookiesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setAlwaysAcceptCookies(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setAppCacheMaximumSizeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ double size = JSValueToNumber(context, arguments[0], NULL);
+ if (!std::isnan(size))
+ controller->setAppCacheMaximumSize(static_cast<unsigned long long>(size));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setAuthenticationPasswordCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> password(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ size_t maxLength = JSStringGetMaximumUTF8CStringSize(password.get());
+ char* passwordBuffer = new char[maxLength + 1];
+ JSStringGetUTF8CString(password.get(), passwordBuffer, maxLength + 1);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setAuthenticationPassword(passwordBuffer);
+ delete[] passwordBuffer;
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setAuthenticationUsernameCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> username(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ size_t maxLength = JSStringGetMaximumUTF8CStringSize(username.get());
+ char* usernameBuffer = new char[maxLength + 1];
+ JSStringGetUTF8CString(username.get(), usernameBuffer, maxLength + 1);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setAuthenticationUsername(usernameBuffer);
+ delete[] usernameBuffer;
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setAuthorAndUserStylesEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setAuthorAndUserStylesEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setCacheModelCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Mac implementation.
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ int cacheModel = JSValueToNumber(context, arguments[0], exception);
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setCacheModel(cacheModel);
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setCustomPolicyDelegateCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ bool permissive = false;
+ if (argumentCount >= 2)
+ permissive = JSValueToBoolean(context, arguments[1]);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setCustomPolicyDelegate(JSValueToBoolean(context, arguments[0]), permissive);
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setDatabaseQuotaCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ double quota = JSValueToNumber(context, arguments[0], NULL);
+ if (!std::isnan(quota))
+ controller->setDatabaseQuota(static_cast<unsigned long long>(quota));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setDeferMainResourceDataLoadCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Mac and Windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDeferMainResourceDataLoad(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setDefersLoadingCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDefersLoading(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setUseDeferredFrameLoadingCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setUseDeferredFrameLoading(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setDomainRelaxationForbiddenForURLSchemeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Mac and Windows implementation
+ if (argumentCount < 2)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ bool forbidden = JSValueToBoolean(context, arguments[0]);
+ JSRetainPtr<JSStringRef> scheme(Adopt, JSValueToStringCopy(context, arguments[1], 0));
+ controller->setDomainRelaxationForbiddenForURLScheme(forbidden, scheme.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setMockDeviceOrientationCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 6)
+ return JSValueMakeUndefined(context);
+
+ bool canProvideAlpha = JSValueToBoolean(context, arguments[0]);
+ double alpha = JSValueToNumber(context, arguments[1], exception);
+ ASSERT(!*exception);
+ bool canProvideBeta = JSValueToBoolean(context, arguments[2]);
+ double beta = JSValueToNumber(context, arguments[3], exception);
+ ASSERT(!*exception);
+ bool canProvideGamma = JSValueToBoolean(context, arguments[4]);
+ double gamma = JSValueToNumber(context, arguments[5], exception);
+ ASSERT(!*exception);
+
+ TestRunner* controller = reinterpret_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setMockGeolocationPositionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 3)
+ return JSValueMakeUndefined(context);
+
+ double latitude = JSValueToNumber(context, arguments[0], 0);
+ double longitude = JSValueToNumber(context, arguments[1], 0);
+ double accuracy = JSValueToNumber(context, arguments[2], 0);
+
+ bool canProvideAltitude = false;
+ double altitude = 0.;
+ if (argumentCount > 3 && !JSValueIsUndefined(context, arguments[3])) {
+ canProvideAltitude = true;
+ altitude = JSValueToNumber(context, arguments[3], 0);
+ }
+
+ bool canProvideAltitudeAccuracy = false;
+ double altitudeAccuracy = 0.;
+ if (argumentCount > 4 && !JSValueIsUndefined(context, arguments[4])) {
+ canProvideAltitudeAccuracy = true;
+ altitudeAccuracy = JSValueToNumber(context, arguments[4], 0);
+ }
+
+ bool canProvideHeading = false;
+ double heading = 0.;
+ if (argumentCount > 5 && !JSValueIsUndefined(context, arguments[5])) {
+ canProvideHeading = true;
+ heading = JSValueToNumber(context, arguments[5], 0);
+ }
+
+ bool canProvideSpeed = false;
+ double speed = 0.;
+ if (argumentCount > 6 && !JSValueIsUndefined(context, arguments[6])) {
+ canProvideSpeed = true;
+ speed = JSValueToNumber(context, arguments[6], 0);
+ }
+
+ TestRunner* controller = reinterpret_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setMockGeolocationPosition(latitude, longitude, accuracy, canProvideAltitude, altitude, canProvideAltitudeAccuracy, altitudeAccuracy, canProvideHeading, heading, canProvideSpeed, speed);
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setMockGeolocationPositionUnavailableErrorCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> message(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = reinterpret_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setMockGeolocationPositionUnavailableError(message.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setNewWindowsCopyBackForwardListCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setNewWindowsCopyBackForwardList(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setGeolocationPermissionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setGeolocationPermission(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setHandlesAuthenticationChallengesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setHandlesAuthenticationChallenges(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setPOSIXLocaleCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ JSRetainPtr<JSStringRef> locale(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ controller->setPOSIXLocale(locale.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setIconDatabaseEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setIconDatabaseEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setMainFrameIsFirstResponderCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setMainFrameIsFirstResponder(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setPersistentUserStyleSheetLocationCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> path(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setPersistentUserStyleSheetLocation(path.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setPrivateBrowsingEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setPrivateBrowsingEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setJavaScriptCanAccessClipboardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setJavaScriptCanAccessClipboard(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setXSSAuditorEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setXSSAuditorEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setSpatialNavigationEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation.
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setSpatialNavigationEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setPrintingCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setIsPrinting(true);
+ return JSValueMakeUndefined(context);
+}
+
+
+static JSValueRef setAllowUniversalAccessFromFileURLsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setAllowUniversalAccessFromFileURLs(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setAllowFileAccessFromFileURLsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setAllowFileAccessFromFileURLs(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setTabKeyCyclesThroughElementsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setTabKeyCyclesThroughElements(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+#if PLATFORM(IOS)
+static JSValueRef setTelephoneNumberParsingEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setTelephoneNumberParsingEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setPagePausedCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setPagePaused(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+#endif
+
+#if ENABLE(IOS_TEXT_AUTOSIZING)
+static JSValueRef setTextAutosizingEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setTextAutosizingEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+#endif
+
+static JSValueRef setUseDashboardCompatibilityModeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setUseDashboardCompatibilityMode(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setUserStyleSheetEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setUserStyleSheetEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setUserStyleSheetLocationCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> path(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setUserStyleSheetLocation(path.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setValueForUserCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount != 2)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> value(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setValueForUser(context, arguments[0], value.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setViewModeMediaFeatureCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> mode(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setViewModeMediaFeature(mode.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setWillSendRequestClearHeaderCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> header(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ size_t maxLength = JSStringGetMaximumUTF8CStringSize(header.get());
+ auto headerBuffer = std::make_unique<char[]>(maxLength + 1);
+ JSStringGetUTF8CString(header.get(), headerBuffer.get(), maxLength + 1);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setWillSendRequestClearHeader(headerBuffer.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setWillSendRequestReturnsNullCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has cross-platform implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setWillSendRequestReturnsNull(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setWillSendRequestReturnsNullOnRedirectCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has cross-platform implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setWillSendRequestReturnsNullOnRedirect(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setWindowIsKeyCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setWindowIsKey(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef waitUntilDoneCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setWaitToDump(true);
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef windowCountCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac implementation
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ int windows = controller->windowCount();
+ return JSValueMakeNumber(context, windows);
+}
+
+static JSValueRef setPopupBlockingEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setPopupBlockingEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setPluginsEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setPluginsEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setPageVisibilityCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> visibility(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ size_t maxLength = JSStringGetMaximumUTF8CStringSize(visibility.get());
+ char* visibilityBuffer = new char[maxLength + 1];
+ JSStringGetUTF8CString(visibility.get(), visibilityBuffer, maxLength + 1);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setPageVisibility(visibilityBuffer);
+ delete[] visibilityBuffer;
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef resetPageVisibilityCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->resetPageVisibility();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setAutomaticLinkDetectionEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setAutomaticLinkDetectionEnabled(JSValueToBoolean(context, arguments[0]));
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setStopProvisionalFrameLoadsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setStopProvisionalFrameLoads(true);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef showWebInspectorCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->showWebInspector();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef closeWebInspectorCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->closeWebInspector();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef evaluateInWebInspectorCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ JSRetainPtr<JSStringRef> script(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ controller->evaluateInWebInspector(script.get());
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef evaluateScriptInIsolatedWorldCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ double worldID = JSValueToNumber(context, arguments[0], exception);
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> script(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+
+ controller->evaluateScriptInIsolatedWorld(static_cast<unsigned>(worldID), JSContextGetGlobalObject(context), script.get());
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef evaluateScriptInIsolatedWorldAndReturnValueCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ double worldID = JSValueToNumber(context, arguments[0], exception);
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> script(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+
+ controller->evaluateScriptInIsolatedWorldAndReturnValue(static_cast<unsigned>(worldID), JSContextGetGlobalObject(context), script.get());
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef waitForPolicyDelegateCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t, const JSValueRef[], JSValueRef*)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->waitForPolicyDelegate();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef addOriginAccessWhitelistEntryCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 4)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> sourceOrigin(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> destinationProtocol(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> destinationHost(Adopt, JSValueToStringCopy(context, arguments[2], exception));
+ ASSERT(!*exception);
+ bool allowDestinationSubdomains = JSValueToBoolean(context, arguments[3]);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->addOriginAccessWhitelistEntry(sourceOrigin.get(), destinationProtocol.get(), destinationHost.get(), allowDestinationSubdomains);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef removeOriginAccessWhitelistEntryCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 4)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> sourceOrigin(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> destinationProtocol(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> destinationHost(Adopt, JSValueToStringCopy(context, arguments[2], exception));
+ ASSERT(!*exception);
+ bool allowDestinationSubdomains = JSValueToBoolean(context, arguments[3]);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->removeOriginAccessWhitelistEntry(sourceOrigin.get(), destinationProtocol.get(), destinationHost.get(), allowDestinationSubdomains);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setScrollbarPolicyCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 2)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> orientation(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> policy(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setScrollbarPolicy(orientation.get(), policy.get());
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef addUserScriptCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 3)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> source(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ bool runAtStart = JSValueToBoolean(context, arguments[1]);
+ bool allFrames = JSValueToBoolean(context, arguments[2]);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->addUserScript(source.get(), runAtStart, allFrames);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef addUserStyleSheetCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 2)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> source(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ bool allFrames = JSValueToBoolean(context, arguments[1]);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->addUserStyleSheet(source.get(), allFrames);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setShouldPaintBrokenImageCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setShouldPaintBrokenImage(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef apiTestNewWindowDataLoadBaseURLCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 2)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> utf8Data(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ JSRetainPtr<JSStringRef> baseURL(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->apiTestNewWindowDataLoadBaseURL(utf8Data.get(), baseURL.get());
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef apiTestGoToCurrentBackForwardItemCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->apiTestGoToCurrentBackForwardItem();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setWebViewEditableCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setWebViewEditable(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef abortModalCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->abortModal();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef authenticateSessionCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // authenticateSession(url, username, password)
+ if (argumentCount != 3)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> url(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> username(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> password(Adopt, JSValueToStringCopy(context, arguments[2], exception));
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->authenticateSession(url.get(), username.get(), password.get());
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setSerializeHTTPLoadsCallback(JSContextRef context, JSObjectRef, JSObjectRef, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ bool serialize = true;
+ if (argumentCount == 1)
+ serialize = JSValueToBoolean(context, arguments[0]);
+
+ TestRunner::setSerializeHTTPLoads(serialize);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setShouldStayOnPageAfterHandlingBeforeUnloadCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ if (argumentCount == 1)
+ controller->setShouldStayOnPageAfterHandlingBeforeUnload(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef addChromeInputFieldCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->addChromeInputField();
+ // the first argument is a callback that is called once the input field has been added
+ if (argumentCount == 1)
+ JSObjectCallAsFunction(context, JSValueToObject(context, arguments[0], 0), thisObject, 0, 0, 0);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef removeChromeInputFieldCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->removeChromeInputField();
+ // the first argument is a callback that is called once the input field has been added
+ if (argumentCount == 1)
+ JSObjectCallAsFunction(context, JSValueToObject(context, arguments[0], 0), thisObject, 0, 0, 0);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef focusWebViewCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->focusWebView();
+ // the first argument is a callback that is called once the input field has been added
+ if (argumentCount == 1)
+ JSObjectCallAsFunction(context, JSValueToObject(context, arguments[0], 0), thisObject, 0, 0, 0);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setBackingScaleFactorCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 2)
+ return JSValueMakeUndefined(context);
+
+ double backingScaleFactor = JSValueToNumber(context, arguments[0], exception);
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setBackingScaleFactor(backingScaleFactor);
+
+ // The second argument is a callback that is called once the backing scale factor has been set.
+ JSObjectCallAsFunction(context, JSValueToObject(context, arguments[1], 0), thisObject, 0, 0, 0);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef preciseTimeCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, WTF::currentTime());
+}
+
+// Static Values
+
+static JSValueRef getGlobalFlagCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ return JSValueMakeBoolean(context, controller->globalFlag());
+}
+
+static JSValueRef getDatabaseDefaultQuotaCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ return JSValueMakeNumber(context, controller->databaseDefaultQuota());
+}
+
+static JSValueRef getDatabaseMaxQuotaCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ return JSValueMakeNumber(context, controller->databaseMaxQuota());
+}
+
+static JSValueRef getWebHistoryItemCountCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ return JSValueMakeNumber(context, controller->webHistoryItemCount());
+}
+
+static JSValueRef getSecureEventInputIsEnabledCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+#if PLATFORM(MAC) && !PLATFORM(IOS)
+ return JSValueMakeBoolean(context, IsSecureEventInputEnabled());
+#else
+ return JSValueMakeBoolean(context, false);
+#endif
+}
+
+static JSValueRef getTitleTextDirectionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ JSRetainPtr<JSStringRef> titleDirection(Adopt, JSStringCreateWithUTF8CString(controller->titleTextDirection().c_str()));
+ return JSValueMakeString(context, titleDirection.get());
+}
+
+static bool setGlobalFlagCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setGlobalFlag(JSValueToBoolean(context, value));
+ return true;
+}
+
+static bool setDatabaseDefaultQuotaCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDatabaseDefaultQuota(JSValueToNumber(context, value, exception));
+ ASSERT(!*exception);
+ return true;
+}
+
+static bool setDatabaseMaxQuotaCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setDatabaseMaxQuota(JSValueToNumber(context, value, exception));
+ ASSERT(!*exception);
+ return true;
+}
+
+static JSValueRef ignoreLegacyWebNotificationPermissionRequestsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->ignoreLegacyWebNotificationPermissionRequests();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef simulateLegacyWebNotificationClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ JSRetainPtr<JSStringRef> title(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ controller->simulateLegacyWebNotificationClick(title.get());
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setTextDirectionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount == 1) {
+ JSRetainPtr<JSStringRef> direction(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setTextDirection(direction.get());
+ }
+
+ return JSValueMakeUndefined(context);
+
+}
+
+static JSValueRef setHasCustomFullScreenBehaviorCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount == 1) {
+ bool hasCustomBehavior = JSValueToBoolean(context, arguments[0]);
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setHasCustomFullScreenBehavior(hasCustomBehavior);
+ }
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setStorageDatabaseIdleIntervalCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 1)
+ return JSValueMakeUndefined(context);
+
+ double interval = JSValueToNumber(context, arguments[0], exception);
+ ASSERT(!*exception);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->setStorageDatabaseIdleInterval(interval);
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef closeIdleLocalStorageDatabasesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ controller->closeIdleLocalStorageDatabases();
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef grantWebNotificationPermissionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ JSRetainPtr<JSStringRef> origin(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ controller->grantWebNotificationPermission(origin.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+
+static JSValueRef denyWebNotificationPermissionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ JSRetainPtr<JSStringRef> origin(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ controller->denyWebNotificationPermission(origin.get());
+
+ return JSValueMakeUndefined(context);
+}
+
+
+static JSValueRef removeAllWebNotificationPermissionsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ controller->removeAllWebNotificationPermissions();
+
+ return JSValueMakeUndefined(context);
+}
+
+
+static JSValueRef simulateWebNotificationClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+
+ controller->simulateWebNotificationClick(arguments[0]);
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef numberOfDFGCompiles(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ return JSC::numberOfDFGCompiles(context, arguments[0]);
+}
+
+static JSValueRef neverInlineFunction(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ return JSC::setNeverInline(context, arguments[0]);
+}
+
+static void testRunnerObjectFinalize(JSObjectRef object)
+{
+ TestRunner* controller = static_cast<TestRunner*>(JSObjectGetPrivate(object));
+ controller->deref();
+}
+
+// Object Creation
+
+void TestRunner::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> testRunnerStr(Adopt, JSStringCreateWithUTF8CString("testRunner"));
+ ref();
+
+ JSClassRef classRef = getJSClass();
+ JSValueRef layoutTestContollerObject = JSObjectMake(context, classRef, this);
+ JSClassRelease(classRef);
+
+ JSObjectSetProperty(context, windowObject, testRunnerStr.get(), layoutTestContollerObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
+}
+
+JSClassRef TestRunner::getJSClass()
+{
+ static JSStaticValue* staticValues = TestRunner::staticValues();
+ static JSStaticFunction* staticFunctions = TestRunner::staticFunctions();
+ static JSClassDefinition classDefinition = {
+ 0, kJSClassAttributeNone, "TestRunner", 0, staticValues, staticFunctions,
+ 0, testRunnerObjectFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ return JSClassCreate(&classDefinition);
+}
+
+JSStaticValue* TestRunner::staticValues()
+{
+ static JSStaticValue staticValues[] = {
+ { "globalFlag", getGlobalFlagCallback, setGlobalFlagCallback, kJSPropertyAttributeNone },
+ { "webHistoryItemCount", getWebHistoryItemCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "secureEventInputIsEnabled", getSecureEventInputIsEnabledCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "titleTextDirection", getTitleTextDirectionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "databaseDefaultQuota", getDatabaseDefaultQuotaCallback, setDatabaseDefaultQuotaCallback, kJSPropertyAttributeNone },
+ { "databaseMaxQuota", getDatabaseMaxQuotaCallback, setDatabaseMaxQuotaCallback, kJSPropertyAttributeNone },
+ { 0, 0, 0, 0 }
+ };
+ return staticValues;
+}
+
+JSStaticFunction* TestRunner::staticFunctions()
+{
+ static JSStaticFunction staticFunctions[] = {
+ { "abortModal", abortModalCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "addDisallowedURL", addDisallowedURLCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "addURLToRedirect", addURLToRedirectCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "addUserScript", addUserScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "addUserStyleSheet", addUserStyleSheetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "apiTestNewWindowDataLoadBaseURL", apiTestNewWindowDataLoadBaseURLCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "apiTestGoToCurrentBackForwardItem", apiTestGoToCurrentBackForwardItemCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "applicationCacheDiskUsageForOrigin", applicationCacheDiskUsageForOriginCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "callShouldCloseOnWebView", callShouldCloseOnWebViewCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "clearAllApplicationCaches", clearAllApplicationCachesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "clearAllDatabases", clearAllDatabasesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "clearApplicationCacheForOrigin", clearApplicationCacheForOriginCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "clearBackForwardList", clearBackForwardListCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "clearPersistentUserStyleSheet", clearPersistentUserStyleSheetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "closeWebInspector", closeWebInspectorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "decodeHostName", decodeHostNameCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "disallowIncreaseForApplicationCacheQuota", disallowIncreaseForApplicationCacheQuotaCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dispatchPendingLoadRequests", dispatchPendingLoadRequestsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "display", displayCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "displayInvalidatedRegion", displayInvalidatedRegionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpApplicationCacheDelegateCallbacks", dumpApplicationCacheDelegateCallbacksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpAsText", dumpAsTextCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpBackForwardList", dumpBackForwardListCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpChildFrameScrollPositions", dumpChildFrameScrollPositionsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpChildFramesAsText", dumpChildFramesAsTextCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpDOMAsWebArchive", dumpDOMAsWebArchiveCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpDatabaseCallbacks", dumpDatabaseCallbacksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpEditingCallbacks", dumpEditingCallbacksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpFrameLoadCallbacks", dumpFrameLoadCallbacksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpProgressFinishedCallback", dumpProgressFinishedCallbackCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpUserGestureInFrameLoadCallbacks", dumpUserGestureInFrameLoadCallbacksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpResourceLoadCallbacks", dumpResourceLoadCallbacksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpResourceResponseMIMETypes", dumpResourceResponseMIMETypesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpSelectionRect", dumpSelectionRectCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpSourceAsWebArchive", dumpSourceAsWebArchiveCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpStatusCallbacks", dumpStatusCallbacksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpTitleChanges", dumpTitleChangesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpIconChanges", dumpIconChangesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "dumpWillCacheResponse", dumpWillCacheResponseCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "encodeHostName", encodeHostNameCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "evaluateInWebInspector", evaluateInWebInspectorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "evaluateScriptInIsolatedWorldAndReturnValue", evaluateScriptInIsolatedWorldAndReturnValueCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "evaluateScriptInIsolatedWorld", evaluateScriptInIsolatedWorldCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "execCommand", execCommandCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "findString", findStringCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "originsWithApplicationCache", originsWithApplicationCacheCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "goBack", goBackCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "ignoreLegacyWebNotificationPermissionRequests", ignoreLegacyWebNotificationPermissionRequestsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isGeolocationProviderActive", isGeolocationProviderActiveCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "isCommandEnabled", isCommandEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "keepWebHistory", keepWebHistoryCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "numberOfPendingGeolocationPermissionRequests", numberOfPendingGeolocationPermissionRequestsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "notifyDone", notifyDoneCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "overridePreference", overridePreferenceCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "pathToLocalResource", pathToLocalResourceCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "printToPDF", dumpAsPDFCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "queueBackNavigation", queueBackNavigationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "queueForwardNavigation", queueForwardNavigationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "queueLoad", queueLoadCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "queueLoadHTMLString", queueLoadHTMLStringCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "queueLoadingScript", queueLoadingScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "queueNonLoadingScript", queueNonLoadingScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "queueReload", queueReloadCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "removeAllVisitedLinks", removeAllVisitedLinksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "removeOriginAccessWhitelistEntry", removeOriginAccessWhitelistEntryCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "repaintSweepHorizontally", repaintSweepHorizontallyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "resetPageVisibility", resetPageVisibilityCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAcceptsEditing", setAcceptsEditingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAllowUniversalAccessFromFileURLs", setAllowUniversalAccessFromFileURLsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAllowFileAccessFromFileURLs", setAllowFileAccessFromFileURLsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAlwaysAcceptCookies", setAlwaysAcceptCookiesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAppCacheMaximumSize", setAppCacheMaximumSizeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAudioResult", setAudioResultCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAuthenticationPassword", setAuthenticationPasswordCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAuthenticationUsername", setAuthenticationUsernameCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAuthorAndUserStylesEnabled", setAuthorAndUserStylesEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setCacheModel", setCacheModelCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setCallCloseOnWebViews", setCallCloseOnWebViewsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setCanOpenWindows", setCanOpenWindowsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setCloseRemainingWindowsWhenComplete", setCloseRemainingWindowsWhenCompleteCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setCustomPolicyDelegate", setCustomPolicyDelegateCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setDatabaseQuota", setDatabaseQuotaCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setDeferMainResourceDataLoad", setDeferMainResourceDataLoadCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setDefersLoading", setDefersLoadingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setUseDeferredFrameLoading", setUseDeferredFrameLoadingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setDomainRelaxationForbiddenForURLScheme", setDomainRelaxationForbiddenForURLSchemeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setGeolocationPermission", setGeolocationPermissionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setHandlesAuthenticationChallenges", setHandlesAuthenticationChallengesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setIconDatabaseEnabled", setIconDatabaseEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setAutomaticLinkDetectionEnabled", setAutomaticLinkDetectionEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setMainFrameIsFirstResponder", setMainFrameIsFirstResponderCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setMockDeviceOrientation", setMockDeviceOrientationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setMockGeolocationPositionUnavailableError", setMockGeolocationPositionUnavailableErrorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setMockGeolocationPosition", setMockGeolocationPositionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setNewWindowsCopyBackForwardList", setNewWindowsCopyBackForwardListCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setPageVisibility", setPageVisibilityCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setPOSIXLocale", setPOSIXLocaleCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setPersistentUserStyleSheetLocation", setPersistentUserStyleSheetLocationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setPopupBlockingEnabled", setPopupBlockingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setPluginsEnabled", setPluginsEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setPrinting", setPrintingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setPrivateBrowsingEnabled", setPrivateBrowsingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setSerializeHTTPLoads", setSerializeHTTPLoadsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setSpatialNavigationEnabled", setSpatialNavigationEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setStopProvisionalFrameLoads", setStopProvisionalFrameLoadsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setTabKeyCyclesThroughElements", setTabKeyCyclesThroughElementsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+#if PLATFORM(IOS)
+ { "setTelephoneNumberParsingEnabled", setTelephoneNumberParsingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setPagePaused", setPagePausedCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+#endif
+#if ENABLE(IOS_TEXT_AUTOSIZING)
+ { "setTextAutosizingEnabled", setTextAutosizingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+#endif
+ { "setUseDashboardCompatibilityMode", setUseDashboardCompatibilityModeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setUserStyleSheetEnabled", setUserStyleSheetEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setUserStyleSheetLocation", setUserStyleSheetLocationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setValueForUser", setValueForUserCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setViewModeMediaFeature", setViewModeMediaFeatureCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setWebViewEditable", setWebViewEditableCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setWillSendRequestClearHeader", setWillSendRequestClearHeaderCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setWillSendRequestReturnsNull", setWillSendRequestReturnsNullCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setWillSendRequestReturnsNullOnRedirect", setWillSendRequestReturnsNullOnRedirectCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setWindowIsKey", setWindowIsKeyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setJavaScriptCanAccessClipboard", setJavaScriptCanAccessClipboardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setXSSAuditorEnabled", setXSSAuditorEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "showWebInspector", showWebInspectorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "simulateLegacyWebNotificationClick", simulateLegacyWebNotificationClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "testOnscreen", testOnscreenCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "testRepaint", testRepaintCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "waitForPolicyDelegate", waitForPolicyDelegateCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "waitUntilDone", waitUntilDoneCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "windowCount", windowCountCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "addOriginAccessWhitelistEntry", addOriginAccessWhitelistEntryCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setScrollbarPolicy", setScrollbarPolicyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "authenticateSession", authenticateSessionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setShouldPaintBrokenImage", setShouldPaintBrokenImageCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setTextDirection", setTextDirectionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setShouldStayOnPageAfterHandlingBeforeUnload", setShouldStayOnPageAfterHandlingBeforeUnloadCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "addChromeInputField", addChromeInputFieldCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "removeChromeInputField", removeChromeInputFieldCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "focusWebView", focusWebViewCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setBackingScaleFactor", setBackingScaleFactorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "preciseTime", preciseTimeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setHasCustomFullScreenBehavior", setHasCustomFullScreenBehaviorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setStorageDatabaseIdleInterval", setStorageDatabaseIdleIntervalCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "closeIdleLocalStorageDatabases", closeIdleLocalStorageDatabasesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "grantWebNotificationPermission", grantWebNotificationPermissionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "denyWebNotificationPermission", denyWebNotificationPermissionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "removeAllWebNotificationPermissions", removeAllWebNotificationPermissionsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "simulateWebNotificationClick", simulateWebNotificationClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "numberOfDFGCompiles", numberOfDFGCompiles, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "neverInlineFunction", neverInlineFunction, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { 0, 0, 0 }
+ };
+
+ return staticFunctions;
+}
+
+void TestRunner::queueLoadHTMLString(JSStringRef content, JSStringRef baseURL)
+{
+ WorkQueue::singleton().queue(new LoadHTMLStringItem(content, baseURL));
+}
+
+void TestRunner::queueLoadAlternateHTMLString(JSStringRef content, JSStringRef baseURL, JSStringRef unreachableURL)
+{
+ WorkQueue::singleton().queue(new LoadHTMLStringItem(content, baseURL, unreachableURL));
+}
+
+void TestRunner::queueBackNavigation(int howFarBack)
+{
+ WorkQueue::singleton().queue(new BackItem(howFarBack));
+}
+
+void TestRunner::queueForwardNavigation(int howFarForward)
+{
+ WorkQueue::singleton().queue(new ForwardItem(howFarForward));
+}
+
+void TestRunner::queueLoadingScript(JSStringRef script)
+{
+ WorkQueue::singleton().queue(new LoadingScriptItem(script));
+}
+
+void TestRunner::queueNonLoadingScript(JSStringRef script)
+{
+ WorkQueue::singleton().queue(new NonLoadingScriptItem(script));
+}
+
+void TestRunner::queueReload()
+{
+ WorkQueue::singleton().queue(new ReloadItem);
+}
+
+void TestRunner::ignoreLegacyWebNotificationPermissionRequests()
+{
+ m_areLegacyWebNotificationPermissionRequestsIgnored = false;
+}
+
+void TestRunner::waitToDumpWatchdogTimerFired()
+{
+ const char* message = "FAIL: Timed out waiting for notifyDone to be called\n";
+ fprintf(stdout, "%s", message);
+ notifyDone();
+}
+
+void TestRunner::setGeolocationPermissionCommon(bool allow)
+{
+ m_isGeolocationPermissionSet = true;
+ m_geolocationPermission = allow;
+}
+
+void TestRunner::setPOSIXLocale(JSStringRef locale)
+{
+ char localeBuf[32];
+ JSStringGetUTF8CString(locale, localeBuf, sizeof(localeBuf));
+ setlocale(LC_ALL, localeBuf);
+}
+
+void TestRunner::addURLToRedirect(std::string origin, std::string destination)
+{
+ m_URLsToRedirect[origin] = destination;
+}
+
+const std::string& TestRunner::redirectionDestinationForURL(std::string origin)
+{
+ return m_URLsToRedirect[origin];
+}
+
+void TestRunner::setShouldPaintBrokenImage(bool shouldPaintBrokenImage)
+{
+ m_shouldPaintBrokenImage = shouldPaintBrokenImage;
+}
diff --git a/Tools/DumpRenderTree/TestRunner.h b/Tools/DumpRenderTree/TestRunner.h
new file mode 100644
index 000000000..8e6d74128
--- /dev/null
+++ b/Tools/DumpRenderTree/TestRunner.h
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TestRunner_h
+#define TestRunner_h
+
+#include <JavaScriptCore/JSObjectRef.h>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+class TestRunner : public RefCounted<TestRunner> {
+public:
+ static PassRefPtr<TestRunner> create(const std::string& testURL, const std::string& expectedPixelHash);
+
+ static const unsigned viewWidth;
+ static const unsigned viewHeight;
+
+ static const unsigned w3cSVGViewWidth;
+ static const unsigned w3cSVGViewHeight;
+
+ ~TestRunner();
+
+ void makeWindowObject(JSContextRef, JSObjectRef windowObject, JSValueRef* exception);
+
+ void addDisallowedURL(JSStringRef url);
+ const std::set<std::string>& allowedHosts() const { return m_allowedHosts; }
+ void setAllowedHosts(std::set<std::string> hosts) { m_allowedHosts = WTF::move(hosts); }
+ void addURLToRedirect(std::string origin, std::string destination);
+ const std::string& redirectionDestinationForURL(std::string);
+ void clearAllApplicationCaches();
+ void clearAllDatabases();
+ void clearApplicationCacheForOrigin(JSStringRef name);
+ void clearBackForwardList();
+ void clearPersistentUserStyleSheet();
+ bool callShouldCloseOnWebView();
+ JSStringRef copyDecodedHostName(JSStringRef name);
+ JSStringRef copyEncodedHostName(JSStringRef name);
+ void dispatchPendingLoadRequests();
+ void display();
+ void displayInvalidatedRegion();
+ void execCommand(JSStringRef name, JSStringRef value);
+ bool findString(JSContextRef, JSStringRef, JSObjectRef optionsArray);
+ void goBack();
+ JSValueRef originsWithApplicationCache(JSContextRef);
+ long long applicationCacheDiskUsageForOrigin(JSStringRef name);
+ bool isCommandEnabled(JSStringRef name);
+ void keepWebHistory();
+ void notifyDone();
+ int numberOfPendingGeolocationPermissionRequests();
+ bool isGeolocationProviderActive();
+ void overridePreference(JSStringRef key, JSStringRef value);
+ JSStringRef pathToLocalResource(JSContextRef, JSStringRef url);
+ void queueBackNavigation(int howFarBackward);
+ void queueForwardNavigation(int howFarForward);
+ void queueLoad(JSStringRef url, JSStringRef target);
+ void queueLoadHTMLString(JSStringRef content, JSStringRef baseURL);
+ void queueLoadAlternateHTMLString(JSStringRef content, JSStringRef baseURL, JSStringRef unreachableURL);
+ void queueLoadingScript(JSStringRef script);
+ void queueNonLoadingScript(JSStringRef script);
+ void queueReload();
+ void removeAllVisitedLinks();
+ void setAcceptsEditing(bool);
+ void setAllowUniversalAccessFromFileURLs(bool);
+ void setAllowFileAccessFromFileURLs(bool);
+ void setAppCacheMaximumSize(unsigned long long quota);
+ void setAuthorAndUserStylesEnabled(bool);
+ void setCacheModel(int);
+ void setCustomPolicyDelegate(bool setDelegate, bool permissive);
+ void setDatabaseQuota(unsigned long long quota);
+ void setDomainRelaxationForbiddenForURLScheme(bool forbidden, JSStringRef scheme);
+ void setDefersLoading(bool);
+ void setIconDatabaseEnabled(bool);
+ void setJavaScriptCanAccessClipboard(bool flag);
+ void setAutomaticLinkDetectionEnabled(bool flag);
+ void setMainFrameIsFirstResponder(bool flag);
+ void setMockDeviceOrientation(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma);
+ void setMockGeolocationPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed);
+ void setMockGeolocationPositionUnavailableError(JSStringRef message);
+ void setPersistentUserStyleSheetLocation(JSStringRef path);
+ void setPluginsEnabled(bool);
+ void setPopupBlockingEnabled(bool);
+ void setPrivateBrowsingEnabled(bool);
+ void setTabKeyCyclesThroughElements(bool);
+ void setUseDashboardCompatibilityMode(bool flag);
+ void setUserStyleSheetEnabled(bool flag);
+ void setUserStyleSheetLocation(JSStringRef path);
+ void setValueForUser(JSContextRef, JSValueRef nodeObject, JSStringRef value);
+ void setViewModeMediaFeature(JSStringRef);
+ void setXSSAuditorEnabled(bool flag);
+ void setSpatialNavigationEnabled(bool);
+ void setScrollbarPolicy(JSStringRef orientation, JSStringRef policy);
+#if PLATFORM(IOS)
+ void setTelephoneNumberParsingEnabled(bool enable);
+ void setPagePaused(bool paused);
+#endif
+
+ void setPageVisibility(const char*);
+ void resetPageVisibility();
+
+ void waitForPolicyDelegate();
+ size_t webHistoryItemCount();
+ int windowCount();
+
+#if ENABLE(IOS_TEXT_AUTOSIZING)
+ void setTextAutosizingEnabled(bool);
+#endif
+
+ // Legacy here refers to the old TestRunner API for handling web notifications, not the legacy web notification API.
+ void ignoreLegacyWebNotificationPermissionRequests();
+ // Legacy here refers to the old TestRunner API for handling web notifications, not the legacy web notification API.
+ void simulateLegacyWebNotificationClick(JSStringRef title);
+ void grantWebNotificationPermission(JSStringRef origin);
+ void denyWebNotificationPermission(JSStringRef origin);
+ void removeAllWebNotificationPermissions();
+ void simulateWebNotificationClick(JSValueRef notification);
+
+ bool dumpAsAudio() const { return m_dumpAsAudio; }
+ void setDumpAsAudio(bool dumpAsAudio) { m_dumpAsAudio = dumpAsAudio; }
+
+ bool dumpAsPDF() const { return m_dumpAsPDF; }
+ void setDumpAsPDF(bool dumpAsPDF) { m_dumpAsPDF = dumpAsPDF; }
+
+ bool dumpAsText() const { return m_dumpAsText; }
+ void setDumpAsText(bool dumpAsText) { m_dumpAsText = dumpAsText; }
+
+ bool generatePixelResults() const { return m_generatePixelResults; }
+ void setGeneratePixelResults(bool generatePixelResults) { m_generatePixelResults = generatePixelResults; }
+
+ bool disallowIncreaseForApplicationCacheQuota() const { return m_disallowIncreaseForApplicationCacheQuota; }
+ void setDisallowIncreaseForApplicationCacheQuota(bool disallowIncrease) { m_disallowIncreaseForApplicationCacheQuota = disallowIncrease; }
+
+ bool dumpApplicationCacheDelegateCallbacks() const { return m_dumpApplicationCacheDelegateCallbacks; }
+ void setDumpApplicationCacheDelegateCallbacks(bool dumpCallbacks) { m_dumpApplicationCacheDelegateCallbacks = dumpCallbacks; }
+
+ bool dumpBackForwardList() const { return m_dumpBackForwardList; }
+ void setDumpBackForwardList(bool dumpBackForwardList) { m_dumpBackForwardList = dumpBackForwardList; }
+
+ bool dumpChildFrameScrollPositions() const { return m_dumpChildFrameScrollPositions; }
+ void setDumpChildFrameScrollPositions(bool dumpChildFrameScrollPositions) { m_dumpChildFrameScrollPositions = dumpChildFrameScrollPositions; }
+
+ bool dumpChildFramesAsText() const { return m_dumpChildFramesAsText; }
+ void setDumpChildFramesAsText(bool dumpChildFramesAsText) { m_dumpChildFramesAsText = dumpChildFramesAsText; }
+
+ bool dumpDatabaseCallbacks() const { return m_dumpDatabaseCallbacks; }
+ void setDumpDatabaseCallbacks(bool dumpDatabaseCallbacks) { m_dumpDatabaseCallbacks = dumpDatabaseCallbacks; }
+
+ bool dumpDOMAsWebArchive() const { return m_dumpDOMAsWebArchive; }
+ void setDumpDOMAsWebArchive(bool dumpDOMAsWebArchive) { m_dumpDOMAsWebArchive = dumpDOMAsWebArchive; }
+
+ bool dumpEditingCallbacks() const { return m_dumpEditingCallbacks; }
+ void setDumpEditingCallbacks(bool dumpEditingCallbacks) { m_dumpEditingCallbacks = dumpEditingCallbacks; }
+
+ bool dumpFrameLoadCallbacks() const { return m_dumpFrameLoadCallbacks; }
+ void setDumpFrameLoadCallbacks(bool dumpFrameLoadCallbacks) { m_dumpFrameLoadCallbacks = dumpFrameLoadCallbacks; }
+
+ bool dumpProgressFinishedCallback() const { return m_dumpProgressFinishedCallback; }
+ void setDumpProgressFinishedCallback(bool dumpProgressFinishedCallback) { m_dumpProgressFinishedCallback = dumpProgressFinishedCallback; }
+
+ bool dumpUserGestureInFrameLoadCallbacks() const { return m_dumpUserGestureInFrameLoadCallbacks; }
+ void setDumpUserGestureInFrameLoadCallbacks(bool dumpUserGestureInFrameLoadCallbacks) { m_dumpUserGestureInFrameLoadCallbacks = dumpUserGestureInFrameLoadCallbacks; }
+
+ bool dumpHistoryDelegateCallbacks() const { return m_dumpHistoryDelegateCallbacks; }
+ void setDumpHistoryDelegateCallbacks(bool dumpHistoryDelegateCallbacks) { m_dumpHistoryDelegateCallbacks = dumpHistoryDelegateCallbacks; }
+
+ bool dumpResourceLoadCallbacks() const { return m_dumpResourceLoadCallbacks; }
+ void setDumpResourceLoadCallbacks(bool dumpResourceLoadCallbacks) { m_dumpResourceLoadCallbacks = dumpResourceLoadCallbacks; }
+
+ bool dumpResourceResponseMIMETypes() const { return m_dumpResourceResponseMIMETypes; }
+ void setDumpResourceResponseMIMETypes(bool dumpResourceResponseMIMETypes) { m_dumpResourceResponseMIMETypes = dumpResourceResponseMIMETypes; }
+
+ bool dumpSelectionRect() const { return m_dumpSelectionRect; }
+ void setDumpSelectionRect(bool dumpSelectionRect) { m_dumpSelectionRect = dumpSelectionRect; }
+
+ bool dumpSourceAsWebArchive() const { return m_dumpSourceAsWebArchive; }
+ void setDumpSourceAsWebArchive(bool dumpSourceAsWebArchive) { m_dumpSourceAsWebArchive = dumpSourceAsWebArchive; }
+
+ bool dumpStatusCallbacks() const { return m_dumpStatusCallbacks; }
+ void setDumpStatusCallbacks(bool dumpStatusCallbacks) { m_dumpStatusCallbacks = dumpStatusCallbacks; }
+
+ bool dumpTitleChanges() const { return m_dumpTitleChanges; }
+ void setDumpTitleChanges(bool dumpTitleChanges) { m_dumpTitleChanges = dumpTitleChanges; }
+
+ bool dumpIconChanges() const { return m_dumpIconChanges; }
+ void setDumpIconChanges(bool dumpIconChanges) { m_dumpIconChanges = dumpIconChanges; }
+
+ bool dumpVisitedLinksCallback() const { return m_dumpVisitedLinksCallback; }
+ void setDumpVisitedLinksCallback(bool dumpVisitedLinksCallback) { m_dumpVisitedLinksCallback = dumpVisitedLinksCallback; }
+
+ bool dumpWillCacheResponse() const { return m_dumpWillCacheResponse; }
+ void setDumpWillCacheResponse(bool dumpWillCacheResponse) { m_dumpWillCacheResponse = dumpWillCacheResponse; }
+
+ bool callCloseOnWebViews() const { return m_callCloseOnWebViews; }
+ void setCallCloseOnWebViews(bool callCloseOnWebViews) { m_callCloseOnWebViews = callCloseOnWebViews; }
+
+ bool canOpenWindows() const { return m_canOpenWindows; }
+ void setCanOpenWindows(bool canOpenWindows) { m_canOpenWindows = canOpenWindows; }
+
+ bool closeRemainingWindowsWhenComplete() const { return m_closeRemainingWindowsWhenComplete; }
+ void setCloseRemainingWindowsWhenComplete(bool closeRemainingWindowsWhenComplete) { m_closeRemainingWindowsWhenComplete = closeRemainingWindowsWhenComplete; }
+
+ bool newWindowsCopyBackForwardList() const { return m_newWindowsCopyBackForwardList; }
+ void setNewWindowsCopyBackForwardList(bool newWindowsCopyBackForwardList) { m_newWindowsCopyBackForwardList = newWindowsCopyBackForwardList; }
+
+ bool stopProvisionalFrameLoads() const { return m_stopProvisionalFrameLoads; }
+ void setStopProvisionalFrameLoads(bool stopProvisionalFrameLoads) { m_stopProvisionalFrameLoads = stopProvisionalFrameLoads; }
+
+ bool testOnscreen() const { return m_testOnscreen; }
+ void setTestOnscreen(bool testOnscreen) { m_testOnscreen = testOnscreen; }
+
+ bool testRepaint() const { return m_testRepaint; }
+ void setTestRepaint(bool testRepaint) { m_testRepaint = testRepaint; }
+
+ bool testRepaintSweepHorizontally() const { return m_testRepaintSweepHorizontally; }
+ void setTestRepaintSweepHorizontally(bool testRepaintSweepHorizontally) { m_testRepaintSweepHorizontally = testRepaintSweepHorizontally; }
+
+ bool waitToDump() const { return m_waitToDump; }
+ void setWaitToDump(bool);
+ void waitToDumpWatchdogTimerFired();
+
+ const std::set<std::string>& willSendRequestClearHeaders() const { return m_willSendRequestClearHeaders; }
+ void setWillSendRequestClearHeader(std::string header) { m_willSendRequestClearHeaders.insert(header); }
+
+ bool willSendRequestReturnsNull() const { return m_willSendRequestReturnsNull; }
+ void setWillSendRequestReturnsNull(bool returnsNull) { m_willSendRequestReturnsNull = returnsNull; }
+
+ bool willSendRequestReturnsNullOnRedirect() const { return m_willSendRequestReturnsNullOnRedirect; }
+ void setWillSendRequestReturnsNullOnRedirect(bool returnsNull) { m_willSendRequestReturnsNullOnRedirect = returnsNull; }
+
+ bool windowIsKey() const { return m_windowIsKey; }
+ void setWindowIsKey(bool);
+
+ bool alwaysAcceptCookies() const { return m_alwaysAcceptCookies; }
+ void setAlwaysAcceptCookies(bool);
+
+ bool handlesAuthenticationChallenges() const { return m_handlesAuthenticationChallenges; }
+ void setHandlesAuthenticationChallenges(bool handlesAuthenticationChallenges) { m_handlesAuthenticationChallenges = handlesAuthenticationChallenges; }
+
+ bool isPrinting() const { return m_isPrinting; }
+ void setIsPrinting(bool isPrinting) { m_isPrinting = isPrinting; }
+
+ const std::string& authenticationUsername() const { return m_authenticationUsername; }
+ void setAuthenticationUsername(std::string username) { m_authenticationUsername = username; }
+
+ const std::string& authenticationPassword() const { return m_authenticationPassword; }
+ void setAuthenticationPassword(std::string password) { m_authenticationPassword = password; }
+
+ bool globalFlag() const { return m_globalFlag; }
+ void setGlobalFlag(bool globalFlag) { m_globalFlag = globalFlag; }
+
+ double databaseDefaultQuota() const { return m_databaseDefaultQuota; }
+ void setDatabaseDefaultQuota(double quota) { m_databaseDefaultQuota = quota; }
+
+ double databaseMaxQuota() const { return m_databaseMaxQuota; }
+ void setDatabaseMaxQuota(double quota) { m_databaseMaxQuota = quota; }
+
+ bool deferMainResourceDataLoad() const { return m_deferMainResourceDataLoad; }
+ void setDeferMainResourceDataLoad(bool flag) { m_deferMainResourceDataLoad = flag; }
+
+ bool useDeferredFrameLoading() const { return m_useDeferredFrameLoading; }
+ void setUseDeferredFrameLoading(bool flag) { m_useDeferredFrameLoading = flag; }
+
+ const std::string& testURL() const { return m_testURL; }
+ const std::string& expectedPixelHash() const { return m_expectedPixelHash; }
+
+ const std::vector<char>& audioResult() const { return m_audioResult; }
+ void setAudioResult(const std::vector<char>& audioData) { m_audioResult = audioData; }
+
+ void addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains);
+ void removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains);
+
+ void addUserScript(JSStringRef source, bool runAtStart, bool allFrames);
+ void addUserStyleSheet(JSStringRef source, bool allFrames);
+
+ void setGeolocationPermission(bool allow);
+ bool isGeolocationPermissionSet() const { return m_isGeolocationPermissionSet; }
+ bool geolocationPermission() const { return m_geolocationPermission; }
+
+ void setDeveloperExtrasEnabled(bool);
+ void showWebInspector();
+ void closeWebInspector();
+ void evaluateInWebInspector(JSStringRef script);
+ void evaluateScriptInIsolatedWorld(unsigned worldID, JSObjectRef globalObject, JSStringRef script);
+ void evaluateScriptInIsolatedWorldAndReturnValue(unsigned worldID, JSObjectRef globalObject, JSStringRef script);
+
+ bool shouldStayOnPageAfterHandlingBeforeUnload() const { return m_shouldStayOnPageAfterHandlingBeforeUnload; }
+ void setShouldStayOnPageAfterHandlingBeforeUnload(bool shouldStayOnPageAfterHandlingBeforeUnload) { m_shouldStayOnPageAfterHandlingBeforeUnload = shouldStayOnPageAfterHandlingBeforeUnload; }
+
+ void addChromeInputField();
+ void removeChromeInputField();
+ void focusWebView();
+
+ void setBackingScaleFactor(double);
+
+ void setPOSIXLocale(JSStringRef);
+
+ void setWebViewEditable(bool);
+
+ void abortModal();
+
+ static void setSerializeHTTPLoads(bool);
+
+ // The following API test functions should probably be moved to platform-specific
+ // unit tests outside of DRT once they exist.
+ void apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data, JSStringRef baseURL);
+ void apiTestGoToCurrentBackForwardItem();
+
+ // Simulate a request an embedding application could make, populating per-session credential storage.
+ void authenticateSession(JSStringRef url, JSStringRef username, JSStringRef password);
+
+ void setShouldPaintBrokenImage(bool);
+ bool shouldPaintBrokenImage() const { return m_shouldPaintBrokenImage; }
+
+ void setTextDirection(JSStringRef);
+ const std::string& titleTextDirection() const { return m_titleTextDirection; }
+ void setTitleTextDirection(const std::string& direction) { m_titleTextDirection = direction; }
+
+ // Custom full screen behavior.
+ void setHasCustomFullScreenBehavior(bool value) { m_customFullScreenBehavior = value; }
+ bool hasCustomFullScreenBehavior() const { return m_customFullScreenBehavior; }
+
+ void setStorageDatabaseIdleInterval(double);
+ void closeIdleLocalStorageDatabases();
+
+ bool hasPendingWebNotificationClick() const { return m_hasPendingWebNotificationClick; }
+
+ void setCustomTimeout(int duration) { m_timeout = duration; }
+
+private:
+ TestRunner(const std::string& testURL, const std::string& expectedPixelHash);
+
+ void setGeolocationPermissionCommon(bool allow);
+
+ bool m_disallowIncreaseForApplicationCacheQuota;
+ bool m_dumpApplicationCacheDelegateCallbacks;
+ bool m_dumpAsAudio;
+ bool m_dumpAsPDF;
+ bool m_dumpAsText;
+ bool m_dumpBackForwardList;
+ bool m_dumpChildFrameScrollPositions;
+ bool m_dumpChildFramesAsText;
+ bool m_dumpDOMAsWebArchive;
+ bool m_dumpDatabaseCallbacks;
+ bool m_dumpEditingCallbacks;
+ bool m_dumpFrameLoadCallbacks;
+ bool m_dumpProgressFinishedCallback;
+ bool m_dumpUserGestureInFrameLoadCallbacks;
+ bool m_dumpHistoryDelegateCallbacks;
+ bool m_dumpResourceLoadCallbacks;
+ bool m_dumpResourceResponseMIMETypes;
+ bool m_dumpSelectionRect;
+ bool m_dumpSourceAsWebArchive;
+ bool m_dumpStatusCallbacks;
+ bool m_dumpTitleChanges;
+ bool m_dumpIconChanges;
+ bool m_dumpVisitedLinksCallback;
+ bool m_dumpWillCacheResponse;
+ bool m_generatePixelResults;
+ bool m_callCloseOnWebViews;
+ bool m_canOpenWindows;
+ bool m_closeRemainingWindowsWhenComplete;
+ bool m_newWindowsCopyBackForwardList;
+ bool m_stopProvisionalFrameLoads;
+ bool m_testOnscreen;
+ bool m_testRepaint;
+ bool m_testRepaintSweepHorizontally;
+ bool m_waitToDump; // True if waitUntilDone() has been called, but notifyDone() has not yet been called.
+ bool m_willSendRequestReturnsNull;
+ bool m_willSendRequestReturnsNullOnRedirect;
+ bool m_windowIsKey;
+ bool m_alwaysAcceptCookies;
+ bool m_globalFlag;
+ bool m_isGeolocationPermissionSet;
+ bool m_geolocationPermission;
+ bool m_handlesAuthenticationChallenges;
+ bool m_isPrinting;
+ bool m_deferMainResourceDataLoad;
+ bool m_useDeferredFrameLoading;
+ bool m_shouldPaintBrokenImage;
+ bool m_shouldStayOnPageAfterHandlingBeforeUnload;
+ // FIXME 81697: This variable most likely will be removed once we have migrated the tests from fast/notifications to http/tests/notifications.
+ bool m_areLegacyWebNotificationPermissionRequestsIgnored;
+ bool m_customFullScreenBehavior;
+ bool m_hasPendingWebNotificationClick;
+
+ double m_databaseDefaultQuota;
+ double m_databaseMaxQuota;
+
+ std::string m_authenticationUsername;
+ std::string m_authenticationPassword;
+ std::string m_testURL;
+ std::string m_expectedPixelHash; // empty string if no hash
+ std::string m_titleTextDirection;
+
+ std::set<std::string> m_willSendRequestClearHeaders;
+ std::set<std::string> m_allowedHosts;
+
+ std::vector<char> m_audioResult;
+
+ std::map<std::string, std::string> m_URLsToRedirect;
+
+ static JSClassRef getJSClass();
+ static JSStaticValue* staticValues();
+ static JSStaticFunction* staticFunctions();
+
+ int m_timeout;
+};
+
+#endif // TestRunner_h
diff --git a/Tools/DumpRenderTree/WorkQueue.cpp b/Tools/DumpRenderTree/WorkQueue.cpp
new file mode 100644
index 000000000..097f4cdd2
--- /dev/null
+++ b/Tools/DumpRenderTree/WorkQueue.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WorkQueue.h"
+
+#include "WorkQueueItem.h"
+#include <wtf/Assertions.h>
+#include <wtf/NeverDestroyed.h>
+
+static const unsigned queueLength = 1024;
+
+static WorkQueueItem* theQueue[queueLength];
+static unsigned startOfQueue;
+static unsigned endOfQueue;
+
+WorkQueue& WorkQueue::singleton()
+{
+ static NeverDestroyed<WorkQueue> sharedInstance;
+ return sharedInstance;
+}
+
+WorkQueue::WorkQueue()
+ : m_frozen(false)
+{
+}
+
+void WorkQueue::queue(WorkQueueItem* item)
+{
+ ASSERT(endOfQueue < queueLength);
+ ASSERT(endOfQueue >= startOfQueue);
+
+ if (m_frozen) {
+ delete item;
+ return;
+ }
+
+ theQueue[endOfQueue++] = item;
+}
+
+WorkQueueItem* WorkQueue::dequeue()
+{
+ ASSERT(endOfQueue >= startOfQueue);
+
+ if (startOfQueue == endOfQueue)
+ return 0;
+
+ return theQueue[startOfQueue++];
+}
+
+unsigned WorkQueue::count()
+{
+ return endOfQueue - startOfQueue;
+}
+
+void WorkQueue::clear()
+{
+ for (unsigned i = startOfQueue; i < endOfQueue; ++i) {
+ delete theQueue[i];
+ theQueue[i] = 0;
+ }
+
+ startOfQueue = 0;
+ endOfQueue = 0;
+}
+
+bool WorkQueue::processWork()
+{
+ bool startedLoad = false;
+
+ while (!startedLoad && count()) {
+ WorkQueueItem* item = dequeue();
+ ASSERT(item);
+ startedLoad = item->invoke();
+ delete item;
+ }
+
+ // If we're done and we didn't start a load, then we're really done, so return true.
+ return !startedLoad;
+}
diff --git a/Tools/DumpRenderTree/WorkQueue.h b/Tools/DumpRenderTree/WorkQueue.h
new file mode 100644
index 000000000..0697e7c3e
--- /dev/null
+++ b/Tools/DumpRenderTree/WorkQueue.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WorkQueue_h
+#define WorkQueue_h
+
+#include <wtf/Forward.h>
+
+class WorkQueueItem;
+
+class WorkQueue {
+friend class WTF::NeverDestroyed<WorkQueue>;
+
+public:
+ static WorkQueue& singleton();
+
+ void queue(WorkQueueItem*);
+ WorkQueueItem* dequeue();
+ void clear();
+ unsigned count();
+
+ void setFrozen(bool b) { m_frozen = b; }
+
+ bool processWork(); // Returns true if all work is done, false if we started a load.
+
+private:
+ WorkQueue();
+
+ bool m_frozen;
+};
+
+#endif // !defined(WorkQueue_h)
diff --git a/Tools/DumpRenderTree/WorkQueueItem.h b/Tools/DumpRenderTree/WorkQueueItem.h
new file mode 100644
index 000000000..6a49f593f
--- /dev/null
+++ b/Tools/DumpRenderTree/WorkQueueItem.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WorkQueueItem_h
+#define WorkQueueItem_h
+
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <JavaScriptCore/JSBase.h>
+
+class WorkQueueItem {
+public:
+ virtual ~WorkQueueItem() { }
+ virtual bool invoke() const = 0; // Returns true if this started a load.
+};
+
+class LoadItem : public WorkQueueItem {
+public:
+ LoadItem(const JSStringRef url, const JSStringRef target)
+ : m_url(url)
+ , m_target(target)
+ {
+ }
+
+private:
+ virtual bool invoke() const;
+
+ JSRetainPtr<JSStringRef> m_url;
+ JSRetainPtr<JSStringRef> m_target;
+};
+
+class LoadHTMLStringItem : public WorkQueueItem {
+public:
+ LoadHTMLStringItem(const JSStringRef content, const JSStringRef baseURL)
+ : m_content(content)
+ , m_baseURL(baseURL)
+ {
+ }
+
+ LoadHTMLStringItem(const JSStringRef content, const JSStringRef baseURL, const JSStringRef unreachableURL)
+ : m_content(content)
+ , m_baseURL(baseURL)
+ , m_unreachableURL(unreachableURL)
+ {
+ }
+
+private:
+ virtual bool invoke() const;
+
+ JSRetainPtr<JSStringRef> m_content;
+ JSRetainPtr<JSStringRef> m_baseURL;
+ JSRetainPtr<JSStringRef> m_unreachableURL;
+};
+
+class ReloadItem : public WorkQueueItem {
+private:
+ virtual bool invoke() const;
+};
+
+class ScriptItem : public WorkQueueItem {
+protected:
+ ScriptItem(const JSStringRef script)
+ : m_script(script)
+ {
+ }
+
+protected:
+ virtual bool invoke() const;
+
+private:
+ JSRetainPtr<JSStringRef> m_script;
+};
+
+class LoadingScriptItem : public ScriptItem {
+public:
+ LoadingScriptItem(const JSStringRef script)
+ : ScriptItem(script)
+ {
+ }
+
+private:
+ virtual bool invoke() const { return ScriptItem::invoke(); }
+};
+
+class NonLoadingScriptItem : public ScriptItem {
+public:
+ NonLoadingScriptItem(const JSStringRef script)
+ : ScriptItem(script)
+ {
+ }
+
+private:
+ virtual bool invoke() const { ScriptItem::invoke(); return false; }
+};
+
+class BackForwardItem : public WorkQueueItem {
+protected:
+ BackForwardItem(int howFar)
+ : m_howFar(howFar)
+ {
+ }
+
+private:
+ virtual bool invoke() const;
+
+ int m_howFar;
+};
+
+class BackItem : public BackForwardItem {
+public:
+ BackItem(int howFar)
+ : BackForwardItem(-howFar)
+ {
+ }
+};
+
+class ForwardItem : public BackForwardItem {
+public:
+ ForwardItem(int howFar)
+ : BackForwardItem(howFar)
+ {
+ }
+};
+
+#endif // !defined(WorkQueueItem_h)
diff --git a/Tools/DumpRenderTree/cairo/PixelDumpSupportCairo.cpp b/Tools/DumpRenderTree/cairo/PixelDumpSupportCairo.cpp
new file mode 100644
index 000000000..9c8931075
--- /dev/null
+++ b/Tools/DumpRenderTree/cairo/PixelDumpSupportCairo.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * (C) 2009 Brent Fulgham <bfulgham@webkit.org>
+ * (C) 2010 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PixelDumpSupportCairo.h"
+
+#include "DumpRenderTree.h"
+#include "PixelDumpSupport.h"
+#include <algorithm>
+#include <ctype.h>
+#include <wtf/Assertions.h>
+#include <wtf/MD5.h>
+#include <wtf/RefPtr.h>
+#include <wtf/StringExtras.h>
+
+using namespace std;
+
+static cairo_status_t writeFunction(void* closure, const unsigned char* data, unsigned int length)
+{
+ Vector<unsigned char>* in = reinterpret_cast<Vector<unsigned char>*>(closure);
+ in->append(data, length);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void printPNG(cairo_surface_t* image, const char* checksum)
+{
+ Vector<unsigned char> pixelData;
+ // Only PNG output is supported for now.
+ cairo_surface_write_to_png_stream(image, writeFunction, &pixelData);
+
+ const size_t dataLength = pixelData.size();
+ const unsigned char* data = pixelData.data();
+
+ printPNG(data, dataLength, checksum);
+}
+
+void computeMD5HashStringForBitmapContext(BitmapContext* context, char hashString[33])
+{
+ cairo_t* bitmapContext = context->cairoContext();
+ cairo_surface_t* surface = cairo_get_target(bitmapContext);
+
+ ASSERT(cairo_image_surface_get_format(surface) == CAIRO_FORMAT_ARGB32); // ImageDiff assumes 32 bit RGBA, we must as well.
+
+ size_t pixelsHigh = cairo_image_surface_get_height(surface);
+ size_t pixelsWide = cairo_image_surface_get_width(surface);
+ size_t bytesPerRow = cairo_image_surface_get_stride(surface);
+
+ MD5 md5Context;
+ unsigned char* bitmapData = static_cast<unsigned char*>(cairo_image_surface_get_data(surface));
+ for (unsigned row = 0; row < pixelsHigh; row++) {
+ md5Context.addBytes(bitmapData, 4 * pixelsWide);
+ bitmapData += bytesPerRow;
+ }
+ MD5::Digest hash;
+ md5Context.checksum(hash);
+
+ snprintf(hashString, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7],
+ hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]);
+}
+
+void dumpBitmap(BitmapContext* context, const char* checksum)
+{
+ cairo_surface_t* surface = cairo_get_target(context->cairoContext());
+ printPNG(surface, checksum);
+}
diff --git a/Tools/DumpRenderTree/cairo/PixelDumpSupportCairo.h b/Tools/DumpRenderTree/cairo/PixelDumpSupportCairo.h
new file mode 100644
index 000000000..a2fac882b
--- /dev/null
+++ b/Tools/DumpRenderTree/cairo/PixelDumpSupportCairo.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * (C) 2009 Brent Fulgham <bfulgham@webkit.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PixelDumpSupportCairo_h
+#define PixelDumpSupportCairo_h
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+#if PLATFORM(WIN)
+#include <windows.h>
+#include <cairo-win32.h>
+#elif PLATFORM(GTK) || PLATFORM(EFL)
+#include <cairo.h>
+#endif
+
+#if PLATFORM(WIN)
+typedef HBITMAP PlatformBitmapBuffer;
+#else
+typedef void* PlatformBitmapBuffer;
+#endif
+
+class BitmapContext : public RefCounted<BitmapContext> {
+public:
+ static PassRefPtr<BitmapContext> createByAdoptingBitmapAndContext(PlatformBitmapBuffer buffer, cairo_t* context)
+ {
+ return adoptRef(new BitmapContext(buffer, context));
+ }
+
+ ~BitmapContext()
+ {
+ if (m_buffer)
+#if PLATFORM(WIN)
+ DeleteObject(m_buffer);
+#else
+ free(m_buffer);
+#endif
+ cairo_destroy(m_context);
+ }
+
+ cairo_t* cairoContext() const { return m_context; }
+
+private:
+
+ BitmapContext(PlatformBitmapBuffer buffer, cairo_t* context)
+ : m_buffer(buffer)
+ , m_context(context)
+ {
+ }
+
+ PlatformBitmapBuffer m_buffer;
+ cairo_t* m_context;
+};
+
+#endif // PixelDumpSupportCairo_h
diff --git a/Tools/DumpRenderTree/config.h b/Tools/DumpRenderTree/config.h
new file mode 100644
index 000000000..9e0fe4fd4
--- /dev/null
+++ b/Tools/DumpRenderTree/config.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008 Nuanti Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#define Config_H
+
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H && defined(BUILDING_WITH_CMAKE)
+#include "cmakeconfig.h"
+#endif
+
+#include <WebCore/PlatformExportMacros.h>
+#include <runtime/JSExportMacros.h>
+
+#ifdef __cplusplus
+#undef new
+#undef delete
+#include <wtf/FastMalloc.h>
+#endif
+
+#if PLATFORM(COCOA)
+#define USE_CF 1
+#endif
+
+#if PLATFORM(WIN)
+#define USE_CF 1
+#if PLATFORM(WIN_CAIRO)
+#define USE_CAIRO 1
+#define USE_CURL 1
+#else
+#define USE_CG 1
+#define USE_CFNETWORK 1
+#endif
+
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x601
+
+#undef WINVER
+#define WINVER 0x0601
+
+#undef _WINSOCKAPI_
+#define _WINSOCKAPI_ // Prevent inclusion of winsock.h in windows.h
+#endif // PLATFORM(WIN)
diff --git a/Tools/ImageDiff/CMakeLists.txt b/Tools/ImageDiff/CMakeLists.txt
new file mode 100644
index 000000000..6ce4ed522
--- /dev/null
+++ b/Tools/ImageDiff/CMakeLists.txt
@@ -0,0 +1,20 @@
+set(IMAGE_DIFF_DIR "${TOOLS_DIR}/ImageDiff")
+
+set(IMAGE_DIFF_INCLUDE_DIRECTORIES
+ ${CMAKE_BINARY_DIR}
+ ${WTF_DIR}
+)
+
+set(IMAGE_DIFF_SYSTEM_INCLUDE_DIRECTORIES "")
+
+set(IMAGE_DIFF_LIBRARIES
+ WTF
+)
+
+WEBKIT_INCLUDE_CONFIG_FILES_IF_EXISTS()
+
+include_directories(${IMAGE_DIFF_INCLUDE_DIRECTORIES})
+include_directories(SYSTEM ${IMAGE_DIFF_SYSTEM_INCLUDE_DIRECTORIES})
+add_executable(ImageDiff ${IMAGE_DIFF_SOURCES})
+target_link_libraries(ImageDiff ${IMAGE_DIFF_LIBRARIES})
+set_target_properties(ImageDiff PROPERTIES FOLDER "Tools")
diff --git a/Tools/ImageDiff/PlatformGTK.cmake b/Tools/ImageDiff/PlatformGTK.cmake
new file mode 100644
index 000000000..8b37ca08b
--- /dev/null
+++ b/Tools/ImageDiff/PlatformGTK.cmake
@@ -0,0 +1,11 @@
+set(IMAGE_DIFF_SOURCES
+ ${IMAGE_DIFF_DIR}/gtk/ImageDiff.cpp
+)
+
+list(APPEND IMAGE_DIFF_SYSTEM_INCLUDE_DIRECTORIES
+ ${GTK_INCLUDE_DIRS}
+)
+
+list(APPEND IMAGE_DIFF_LIBRARIES
+ ${GTK_LIBRARIES}
+)
diff --git a/Tools/ImageDiff/gtk/ImageDiff.cpp b/Tools/ImageDiff/gtk/ImageDiff.cpp
new file mode 100644
index 000000000..c15fcfed2
--- /dev/null
+++ b/Tools/ImageDiff/gtk/ImageDiff.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2009 Zan Dobersek <zandobersek@gmail.com>
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <algorithm>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <gdk/gdk.h>
+
+using namespace std;
+
+static double tolerance = 0;
+static GOptionEntry commandLineOptionEntries[] =
+{
+ { "tolerance", 0, 0, G_OPTION_ARG_DOUBLE, &tolerance, "Percentage difference between images before considering them different", "T" },
+ { 0, 0, 0, G_OPTION_ARG_NONE, 0, 0, 0 },
+};
+
+GdkPixbuf* readPixbufFromStdin(long imageSize)
+{
+ unsigned char imageBuffer[2048];
+ GdkPixbufLoader* loader = gdk_pixbuf_loader_new_with_type("png", 0);
+ GError* error = 0;
+
+ while (imageSize > 0) {
+ size_t bytesToRead = min<int>(imageSize, 2048);
+ size_t bytesRead = fread(imageBuffer, 1, bytesToRead, stdin);
+
+ if (!gdk_pixbuf_loader_write(loader, reinterpret_cast<const guchar*>(imageBuffer), bytesRead, &error)) {
+ g_error_free(error);
+ gdk_pixbuf_loader_close(loader, 0);
+ g_object_unref(loader);
+ return 0;
+ }
+
+ imageSize -= static_cast<int>(bytesRead);
+ }
+
+ gdk_pixbuf_loader_close(loader, 0);
+ GdkPixbuf* decodedImage = gdk_pixbuf_loader_get_pixbuf(loader);
+ g_object_ref(decodedImage);
+ return decodedImage;
+}
+
+GdkPixbuf* differenceImageFromDifferenceBuffer(unsigned char* buffer, int width, int height)
+{
+ GdkPixbuf* image = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height);
+ if (!image)
+ return image;
+
+ int rowStride = gdk_pixbuf_get_rowstride(image);
+ unsigned char* diffPixels = gdk_pixbuf_get_pixels(image);
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ unsigned char* diffPixel = diffPixels + (y * rowStride) + (x * 3);
+ diffPixel[0] = diffPixel[1] = diffPixel[2] = *buffer++;
+ }
+ }
+
+ return image;
+}
+
+float calculateDifference(GdkPixbuf* baselineImage, GdkPixbuf* actualImage, GdkPixbuf** differenceImage)
+{
+ int width = gdk_pixbuf_get_width(actualImage);
+ int height = gdk_pixbuf_get_height(actualImage);
+ int numberOfChannels = gdk_pixbuf_get_n_channels(actualImage);
+ if ((width != gdk_pixbuf_get_width(baselineImage))
+ || (height != gdk_pixbuf_get_height(baselineImage))
+ || (numberOfChannels != gdk_pixbuf_get_n_channels(baselineImage))
+ || (gdk_pixbuf_get_has_alpha(actualImage) != gdk_pixbuf_get_has_alpha(baselineImage))) {
+ fprintf(stderr, "Error, test and reference image have different properties.\n");
+ return 100; // Completely different.
+ }
+
+ unsigned char* diffBuffer = static_cast<unsigned char*>(malloc(width * height));
+ float count = 0;
+ float sum = 0;
+ float maxDistance = 0;
+ int actualRowStride = gdk_pixbuf_get_rowstride(actualImage);
+ int baseRowStride = gdk_pixbuf_get_rowstride(baselineImage);
+ unsigned char* actualPixels = gdk_pixbuf_get_pixels(actualImage);
+ unsigned char* basePixels = gdk_pixbuf_get_pixels(baselineImage);
+ unsigned char* currentDiffPixel = diffBuffer;
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ unsigned char* actualPixel = actualPixels + (y * actualRowStride) + (x * numberOfChannels);
+ unsigned char* basePixel = basePixels + (y * baseRowStride) + (x * numberOfChannels);
+
+ float red = (actualPixel[0] - basePixel[0]) / max<float>(255 - basePixel[0], basePixel[0]);
+ float green = (actualPixel[1] - basePixel[1]) / max<float>(255 - basePixel[1], basePixel[1]);
+ float blue = (actualPixel[2] - basePixel[2]) / max<float>(255 - basePixel[2], basePixel[2]);
+ float alpha = (actualPixel[3] - basePixel[3]) / max<float>(255 - basePixel[3], basePixel[3]);
+ float distance = sqrtf(red * red + green * green + blue * blue + alpha * alpha) / 2.0f;
+
+ *currentDiffPixel++ = (unsigned char)(distance * 255.0f);
+
+ if (distance >= 1.0f / 255.0f) {
+ count += 1.0f;
+ sum += distance;
+ maxDistance = max<float>(maxDistance, distance);
+ }
+ }
+ }
+
+ // Compute the difference as a percentage combining both the number of
+ // different pixels and their difference amount i.e. the average distance
+ // over the entire image
+ float difference = 0;
+ if (count > 0.0f)
+ difference = 100.0f * sum / (height * width);
+ if (difference <= tolerance)
+ difference = 0;
+ else {
+ difference = roundf(difference * 100.0f) / 100.0f;
+ difference = max(difference, 0.01f); // round to 2 decimal places
+ *differenceImage = differenceImageFromDifferenceBuffer(diffBuffer, width, height);
+ }
+
+ free(diffBuffer);
+ return difference;
+}
+
+void printImage(GdkPixbuf* image)
+{
+ char* buffer;
+ gsize bufferSize;
+ GError* error = 0;
+ if (!gdk_pixbuf_save_to_buffer(image, &buffer, &bufferSize, "png", &error, NULL)) {
+ g_error_free(error);
+ return; // Don't bail out, as we can still use the percentage output.
+ }
+
+ printf("Content-Length: %" G_GSIZE_FORMAT "\n", bufferSize);
+ fwrite(buffer, 1, bufferSize, stdout);
+}
+
+void printImageDifferences(GdkPixbuf* baselineImage, GdkPixbuf* actualImage)
+{
+ GdkPixbuf* differenceImage = 0;
+ float difference = calculateDifference(baselineImage, actualImage, &differenceImage);
+ if (difference > 0.0f) {
+ if (differenceImage) {
+ printImage(differenceImage);
+ g_object_unref(differenceImage);
+ }
+ printf("diff: %01.2f%% failed\n", difference);
+ } else
+ printf("diff: %01.2f%% passed\n", difference);
+}
+
+int main(int argc, char* argv[])
+{
+ GError* error = 0;
+ GOptionContext* context = g_option_context_new("- compare two image files, printing their percentage difference and the difference image to stdout");
+ g_option_context_add_main_entries(context, commandLineOptionEntries, 0);
+ if (!g_option_context_parse(context, &argc, &argv, &error)) {
+ printf("Option parsing failed: %s\n", error->message);
+ g_error_free(error);
+ return 1;
+ }
+
+ GdkPixbuf* actualImage = 0;
+ GdkPixbuf* baselineImage = 0;
+ char buffer[2048];
+ while (fgets(buffer, sizeof(buffer), stdin)) {
+ // Convert the first newline into a NUL character so that strtok doesn't produce it.
+ char* newLineCharacter = strchr(buffer, '\n');
+ if (newLineCharacter)
+ *newLineCharacter = '\0';
+
+ if (!strncmp("Content-Length: ", buffer, 16)) {
+ gchar** tokens = g_strsplit(buffer, " ", 0);
+ if (!tokens[1]) {
+ g_strfreev(tokens);
+ printf("Error, image size must be specified..\n");
+ return 1;
+ }
+
+ long imageSize = strtol(tokens[1], 0, 10);
+ g_strfreev(tokens);
+ if (imageSize > 0 && !actualImage) {
+ if (!(actualImage = readPixbufFromStdin(imageSize))) {
+ printf("Error, could not read actual image.\n");
+ return 1;
+ }
+ } else if (imageSize > 0 && !baselineImage) {
+ if (!(baselineImage = readPixbufFromStdin(imageSize))) {
+ printf("Error, could not read baseline image.\n");
+ return 1;
+ }
+ } else {
+ printf("Error, image size must be specified..\n");
+ return 1;
+ }
+ }
+
+ if (actualImage && baselineImage) {
+ printImageDifferences(baselineImage, actualImage);
+ g_object_unref(actualImage);
+ g_object_unref(baselineImage);
+ actualImage = 0;
+ baselineImage = 0;
+ }
+
+ fflush(stdout);
+ }
+
+ return 0;
+}
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserCFLite.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserCFLite.vsprops
new file mode 100644
index 000000000..0882216a5
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserCFLite.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserCFLite"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.lib WebKit$(WebKitDLLConfigSuffix).lib CFLite$(LibraryConfigSuffix).lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserCommon.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserCommon.vsprops
new file mode 100644
index 000000000..51275c7a9
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserCommon.vsprops
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserCommon"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(ConfigurationBuildDir)\Include&quot;;&quot;$(WebKitLibrariesDir)\Include&quot;"
+ UsePrecompiledHeader="2"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)\$(ProjectName)$(WebKitConfigSuffix).dll"
+ />
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserCoreFoundation.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserCoreFoundation.vsprops
new file mode 100644
index 000000000..deaa89bc9
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserCoreFoundation.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserCoreFoundation"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.lib WebKit$(WebKitDLLConfigSuffix).lib CoreFoundation$(LibraryConfigSuffix).lib CFNetwork$(LibraryConfigSuffix).lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserDebug.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserDebug.vsprops
new file mode 100644
index 000000000..33f4e05dd
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserDebug.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserDebug"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug.vsprops;.\MiniBrowserCoreFoundation.vsprops;.\MiniBrowserCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserDebugAll.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserDebugAll.vsprops
new file mode 100644
index 000000000..981e59aa8
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserDebugAll.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserDebugAll"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug_all.vsprops;.\MiniBrowserCoreFoundation.vsprops;.\MiniBrowserCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserDebugCairoCFLite.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserDebugCairoCFLite.vsprops
new file mode 100644
index 000000000..485031283
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserDebugCairoCFLite.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserDebugCairoCFLite"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug_wincairo.vsprops;.\MiniBrowserCFLite.vsprops;.\MiniBrowserCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserLauncherCommon.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherCommon.vsprops
new file mode 100644
index 000000000..742780daf
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherCommon.vsprops
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserLauncherCommon"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.lib"
+ OutputFile="$(OutDir)\MiniBrowser$(WebKitConfigSuffix).exe"
+ ProgramDatabaseFile="$(TargetDir)$(TargetName)Launcher.pdb"
+ />
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebug.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebug.vsprops
new file mode 100644
index 000000000..1f30791da
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebug.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserLauncherDebug"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug.vsprops;.\MiniBrowserLauncherCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebugAll.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebugAll.vsprops
new file mode 100644
index 000000000..ff91b6f2b
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebugAll.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserLauncherDebugAll"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug_all.vsprops;.\MiniBrowserLauncherCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebugCairoCFLite.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebugCairoCFLite.vsprops
new file mode 100644
index 000000000..fb4922093
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherDebugCairoCFLite.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserDebugCairoCFLite"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug.vsprops;.\MiniBrowserLauncherCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserLauncherProduction.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherProduction.vsprops
new file mode 100644
index 000000000..6a725b5cc
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherProduction.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserLauncherProduction"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\releaseproduction.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\production.vsprops;.\MiniBrowserLauncherCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserLauncherRelease.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherRelease.vsprops
new file mode 100644
index 000000000..023632587
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherRelease.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserLauncherRelease"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\releaseproduction.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\release.vsprops;.\MiniBrowserLauncherCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserLauncherReleaseCairoCFLite.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherReleaseCairoCFLite.vsprops
new file mode 100644
index 000000000..b5919fe1b
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserLauncherReleaseCairoCFLite.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserLauncherReleaseCairoCFLite"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\releaseproduction.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\release.vsprops;.\MiniBrowserLauncherCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserProduction.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserProduction.vsprops
new file mode 100644
index 000000000..165a3aab1
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserProduction.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserProduction"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\releaseproduction.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\production.vsprops;.\MiniBrowserCoreFoundation.vsprops;.\MiniBrowserCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserRelease.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserRelease.vsprops
new file mode 100644
index 000000000..8bc9a13ca
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserRelease.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserRelease"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\releaseproduction.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\release.vsprops;.\MiniBrowserCoreFoundation.vsprops;.\MiniBrowserCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/Configurations/MiniBrowserReleaseCairoCFLite.vsprops b/Tools/MiniBrowser/Configurations/MiniBrowserReleaseCairoCFLite.vsprops
new file mode 100644
index 000000000..8253f930d
--- /dev/null
+++ b/Tools/MiniBrowser/Configurations/MiniBrowserReleaseCairoCFLite.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowserReleaseCairoCFLite"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\releaseproduction.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\release.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\WinCairo.vsprops;.\MiniBrowserCFLite.vsprops;.\MiniBrowserCommon.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/MiniBrowser/MBToolbarItem.h b/Tools/MiniBrowser/MBToolbarItem.h
new file mode 100644
index 000000000..9971d4c10
--- /dev/null
+++ b/Tools/MiniBrowser/MBToolbarItem.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+@interface MBToolbarItem : NSToolbarItem
+@end
diff --git a/Tools/MiniBrowser/MiniBrowser.entitlements b/Tools/MiniBrowser/MiniBrowser.entitlements
new file mode 100644
index 000000000..ee95ab7e5
--- /dev/null
+++ b/Tools/MiniBrowser/MiniBrowser.entitlements
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+ <key>com.apple.security.network.client</key>
+ <true/>
+</dict>
+</plist>
diff --git a/Tools/MiniBrowser/MiniBrowserWebProcessPlugIn.h b/Tools/MiniBrowser/MiniBrowserWebProcessPlugIn.h
new file mode 100644
index 000000000..ff16bf5dc
--- /dev/null
+++ b/Tools/MiniBrowser/MiniBrowserWebProcessPlugIn.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <WebKit/WKWebProcessPlugIn.h>
+
+#if WK_API_ENABLED
+
+@interface MiniBrowserWebProcessPlugIn : NSObject <WKWebProcessPlugIn>
+
+@end
+
+#endif // WK_API_ENABLED
diff --git a/Tools/MiniBrowser/gtk/BrowserCellRendererVariant.c b/Tools/MiniBrowser/gtk/BrowserCellRendererVariant.c
new file mode 100644
index 000000000..cb2caa35b
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserCellRendererVariant.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "BrowserCellRendererVariant.h"
+#include "BrowserMarshal.h"
+#include <errno.h>
+
+enum {
+ PROP_0,
+
+ PROP_VALUE,
+ PROP_ADJUSTMENT
+};
+
+enum {
+ CHANGED,
+
+ LAST_SIGNAL
+};
+
+struct _BrowserCellRendererVariant {
+ GtkCellRenderer parent;
+
+ GValue *value;
+
+ GtkCellRenderer *textRenderer;
+ GtkCellRenderer *toggleRenderer;
+ GtkCellRenderer *spinRenderer;
+};
+
+struct _BrowserCellRendererVariantClass {
+ GtkCellRendererClass parent;
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE(BrowserCellRendererVariant, browser_cell_renderer_variant, GTK_TYPE_CELL_RENDERER)
+
+static void browserCellRendererVariantFinalize(GObject *object)
+{
+ BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(object);
+
+ g_object_unref(renderer->toggleRenderer);
+ g_object_unref(renderer->spinRenderer);
+ g_object_unref(renderer->textRenderer);
+ if (renderer->value)
+ g_boxed_free(G_TYPE_VALUE, renderer->value);
+
+ G_OBJECT_CLASS(browser_cell_renderer_variant_parent_class)->finalize(object);
+}
+
+static void browserCellRendererVariantGetProperty(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
+{
+ BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(object);
+
+ switch (propId) {
+ case PROP_VALUE:
+ g_value_set_boxed(value, renderer->value);
+ break;
+ case PROP_ADJUSTMENT: {
+ GtkAdjustment *adjustment = NULL;
+ g_object_get(G_OBJECT(renderer->spinRenderer), "adjustment", &adjustment, NULL);
+ if (adjustment) {
+ g_value_set_object(value, adjustment);
+ g_object_unref(adjustment);
+ }
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ }
+}
+
+static void browserCellRendererVariantSetModeForValue(BrowserCellRendererVariant *renderer)
+{
+ if (!renderer->value)
+ return;
+
+ GtkCellRendererMode mode;
+ if (G_VALUE_HOLDS_BOOLEAN(renderer->value))
+ mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
+ else if (G_VALUE_HOLDS_STRING(renderer->value) || G_VALUE_HOLDS_UINT(renderer->value))
+ mode = GTK_CELL_RENDERER_MODE_EDITABLE;
+ else
+ return;
+
+ g_object_set(G_OBJECT(renderer), "mode", mode, NULL);
+}
+
+static void browserCellRendererVariantSetProperty(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
+{
+ BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(object);
+
+ switch (propId) {
+ case PROP_VALUE:
+ if (renderer->value)
+ g_boxed_free(G_TYPE_VALUE, renderer->value);
+ renderer->value = g_value_dup_boxed(value);
+ browserCellRendererVariantSetModeForValue(renderer);
+ break;
+ case PROP_ADJUSTMENT:
+ g_object_set(G_OBJECT(renderer->spinRenderer), "adjustment", g_value_get_object(value), NULL);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ }
+}
+
+static GtkCellRenderer *browserCellRendererVariantGetRendererForValue(BrowserCellRendererVariant *renderer)
+{
+ if (!renderer->value)
+ return NULL;
+
+ if (G_VALUE_HOLDS_BOOLEAN(renderer->value)) {
+ g_object_set(G_OBJECT(renderer->toggleRenderer),
+ "active", g_value_get_boolean(renderer->value),
+ NULL);
+ return renderer->toggleRenderer;
+ }
+
+ if (G_VALUE_HOLDS_STRING(renderer->value)) {
+ g_object_set(G_OBJECT(renderer->textRenderer),
+ "text", g_value_get_string(renderer->value),
+ NULL);
+ return renderer->textRenderer;
+ }
+
+ if (G_VALUE_HOLDS_UINT(renderer->value)) {
+ gchar *text = g_strdup_printf("%u", g_value_get_uint(renderer->value));
+ g_object_set(G_OBJECT(renderer->spinRenderer), "text", text, NULL);
+ g_free(text);
+ return renderer->spinRenderer;
+ }
+
+ return NULL;
+}
+
+static void browserCellRendererVariantCellRendererTextEdited(BrowserCellRendererVariant *renderer, const gchar *path, const gchar *newText)
+{
+ if (!renderer->value)
+ return;
+
+ if (!G_VALUE_HOLDS_STRING(renderer->value))
+ return;
+
+ g_value_set_string(renderer->value, newText);
+ g_signal_emit(renderer, signals[CHANGED], 0, path, renderer->value);
+}
+
+static void browserCellRendererVariantCellRendererSpinEdited(BrowserCellRendererVariant *renderer, const gchar *path, const gchar *newText)
+{
+ if (!renderer->value)
+ return;
+
+ if (!G_VALUE_HOLDS_UINT(renderer->value))
+ return;
+
+ GtkAdjustment *adjustment;
+ g_object_get(G_OBJECT(renderer->spinRenderer), "adjustment", &adjustment, NULL);
+ if (!adjustment)
+ return;
+
+ errno = 0;
+ gchar *endPtr;
+ gdouble value = g_strtod(newText, &endPtr);
+ if (errno || value > gtk_adjustment_get_upper(adjustment) || value < gtk_adjustment_get_lower(adjustment) || endPtr == newText) {
+ g_warning("Invalid input for cell: %s\n", newText);
+ return;
+ }
+
+ g_value_set_uint(renderer->value, (guint)value);
+ g_signal_emit(renderer, signals[CHANGED], 0, path, renderer->value);
+}
+
+static gboolean browserCellRendererVariantCellRendererActivate(GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, const GdkRectangle *bgArea, const GdkRectangle *cellArea, GtkCellRendererState flags)
+{
+ BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(cell);
+
+ if (!renderer->value)
+ return TRUE;
+
+ if (!G_VALUE_HOLDS_BOOLEAN(renderer->value))
+ return TRUE;
+
+ g_value_set_boolean(renderer->value, !g_value_get_boolean(renderer->value));
+ g_signal_emit(renderer, signals[CHANGED], 0, path, renderer->value);
+
+ return TRUE;
+}
+
+static void browserCellRendererVariantCellRendererRender(GtkCellRenderer *cell, cairo_t *cr, GtkWidget *widget, const GdkRectangle *bgArea, const GdkRectangle *cellArea, GtkCellRendererState flags)
+{
+ GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
+ if (!renderer)
+ return;
+
+ GTK_CELL_RENDERER_GET_CLASS(renderer)->render(renderer, cr, widget, bgArea, cellArea, flags);
+}
+
+static GtkCellEditable *browserCellRendererVariantCellRendererStartEditing(GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, const GdkRectangle *bgArea, const GdkRectangle *cellArea, GtkCellRendererState flags)
+{
+ GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
+ if (!renderer)
+ return NULL;
+
+ if (!GTK_CELL_RENDERER_GET_CLASS(renderer)->start_editing)
+ return NULL;
+
+ return GTK_CELL_RENDERER_GET_CLASS(renderer)->start_editing(renderer, event, widget, path, bgArea, cellArea, flags);
+}
+
+static void browserCellRendererVariantCellRendererGetPreferredWidth(GtkCellRenderer *cell, GtkWidget *widget, gint *minimumWidth, gint *naturalWidth)
+{
+ GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
+ if (!renderer)
+ return;
+
+ GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_width(renderer, widget, minimumWidth, naturalWidth);
+}
+
+static void browserCellRendererVariantCellRendererGetPreferredHeight(GtkCellRenderer *cell, GtkWidget *widget, gint *minimumHeight, gint *naturalHeight)
+{
+ GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
+ if (!renderer)
+ return;
+
+ GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_height(renderer, widget, minimumHeight, naturalHeight);
+}
+
+static void browserCellRendererVariantCellRendererGetPreferredWidthForHeight(GtkCellRenderer *cell, GtkWidget *widget, gint height, gint *minimumWidth, gint *naturalWidth)
+{
+ GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
+ if (!renderer)
+ return;
+
+ GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_width_for_height(renderer, widget, height, minimumWidth, naturalWidth);
+}
+
+static void browserCellRendererVariantCellRendererGetPreferredHeightForWidth(GtkCellRenderer *cell, GtkWidget *widget, gint width, gint *minimumHeight, gint *naturalHeight)
+{
+ GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
+ if (!renderer)
+ return;
+
+ GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_height_for_width(renderer, widget, width, minimumHeight, naturalHeight);
+}
+
+static void browserCellRendererVariantCellRendererGetAlignedArea(GtkCellRenderer *cell, GtkWidget *widget, GtkCellRendererState flags, const GdkRectangle *cellArea, GdkRectangle *alignedArea)
+{
+ GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
+ if (!renderer)
+ return;
+
+ GTK_CELL_RENDERER_GET_CLASS(renderer)->get_aligned_area(renderer, widget, flags, cellArea, alignedArea);
+}
+
+static void browser_cell_renderer_variant_init(BrowserCellRendererVariant *renderer)
+{
+ g_object_set(renderer, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
+
+ renderer->toggleRenderer = gtk_cell_renderer_toggle_new();
+ g_object_set(G_OBJECT(renderer->toggleRenderer), "xalign", 0.0, NULL);
+ g_object_ref_sink(renderer->toggleRenderer);
+
+ renderer->textRenderer = gtk_cell_renderer_text_new();
+ g_signal_connect_swapped(renderer->textRenderer, "edited",
+ G_CALLBACK(browserCellRendererVariantCellRendererTextEdited), renderer);
+ g_object_set(G_OBJECT(renderer->textRenderer), "editable", TRUE, NULL);
+ g_object_ref_sink(renderer->textRenderer);
+
+ renderer->spinRenderer = gtk_cell_renderer_spin_new();
+ g_signal_connect_swapped(renderer->spinRenderer, "edited",
+ G_CALLBACK(browserCellRendererVariantCellRendererSpinEdited), renderer);
+ g_object_set(G_OBJECT(renderer->spinRenderer), "editable", TRUE, NULL);
+}
+
+static void browser_cell_renderer_variant_class_init(BrowserCellRendererVariantClass *klass)
+{
+ GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
+ GtkCellRendererClass *cellRendererClass = GTK_CELL_RENDERER_CLASS(klass);
+
+ gobjectClass->get_property = browserCellRendererVariantGetProperty;
+ gobjectClass->set_property = browserCellRendererVariantSetProperty;
+ gobjectClass->finalize = browserCellRendererVariantFinalize;
+
+ cellRendererClass->activate = browserCellRendererVariantCellRendererActivate;
+ cellRendererClass->render = browserCellRendererVariantCellRendererRender;
+ cellRendererClass->start_editing = browserCellRendererVariantCellRendererStartEditing;
+ cellRendererClass->get_preferred_width = browserCellRendererVariantCellRendererGetPreferredWidth;
+ cellRendererClass->get_preferred_height = browserCellRendererVariantCellRendererGetPreferredHeight;
+ cellRendererClass->get_preferred_width_for_height = browserCellRendererVariantCellRendererGetPreferredWidthForHeight;
+ cellRendererClass->get_preferred_height_for_width = browserCellRendererVariantCellRendererGetPreferredHeightForWidth;
+ cellRendererClass->get_aligned_area = browserCellRendererVariantCellRendererGetAlignedArea;
+
+ g_object_class_install_property(gobjectClass,
+ PROP_VALUE,
+ g_param_spec_boxed("value",
+ "Value",
+ "The cell renderer value",
+ G_TYPE_VALUE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property(gobjectClass,
+ PROP_ADJUSTMENT,
+ g_param_spec_object("adjustment",
+ "Adjustment",
+ "The adjustment that holds the value of the spin button",
+ GTK_TYPE_ADJUSTMENT,
+ G_PARAM_READWRITE));
+
+ signals[CHANGED] =
+ g_signal_new("changed",
+ G_TYPE_FROM_CLASS(gobjectClass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ browser_marshal_VOID__STRING_BOXED,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING, G_TYPE_VALUE);
+}
+
+GtkCellRenderer *browser_cell_renderer_variant_new(void)
+{
+ return GTK_CELL_RENDERER(g_object_new(BROWSER_TYPE_CELL_RENDERER_VARIANT, NULL));
+}
+
diff --git a/Tools/MiniBrowser/gtk/BrowserCellRendererVariant.h b/Tools/MiniBrowser/gtk/BrowserCellRendererVariant.h
new file mode 100644
index 000000000..4fce3bb60
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserCellRendererVariant.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BrowserCellRendererVariant_h
+#define BrowserCellRendererVariant_h
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define BROWSER_TYPE_CELL_RENDERER_VARIANT (browser_cell_renderer_variant_get_type())
+#define BROWSER_CELL_RENDERER_VARIANT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BROWSER_TYPE_CELL_RENDERER_VARIANT, BrowserCellRendererVariant))
+#define BROWSER_CELL_RENDERER_VARIANT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), BROWSER_TYPE_CELL_RENDERER_VARIANT, BrowserCellRendererVariantClass))
+#define BROWSER_IS_CELL_RENDERER_VARIANT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BROWSER_TYPE_CELL_RENDERER_VARIANT))
+#define BROWSER_IS_CELL_RENDERER_VARIANT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BROWSER_TYPE_CELL_RENDERER_VARIANT))
+#define BROWSER_CELL_RENDERER_VARIANT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), BROWSER_TYPE_CELL_RENDERER_VARIANT, BrowserCellRendererVariantClass))
+
+typedef struct _BrowserCellRendererVariant BrowserCellRendererVariant;
+typedef struct _BrowserCellRendererVariantClass BrowserCellRendererVariantClass;
+
+GType browser_cell_renderer_variant_get_type(void);
+
+GtkCellRenderer* browser_cell_renderer_variant_new(void);
+
+G_END_DECLS
+
+#endif
diff --git a/Tools/MiniBrowser/gtk/BrowserDownloadsBar.c b/Tools/MiniBrowser/gtk/BrowserDownloadsBar.c
new file mode 100644
index 000000000..b0ec3ce14
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserDownloadsBar.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cmakeconfig.h"
+#include "BrowserDownloadsBar.h"
+
+#include <glib/gi18n.h>
+
+#define BROWSER_TYPE_DOWNLOAD (browser_download_get_type())
+#define BROWSER_DOWNLOAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BROWSER_TYPE_DOWNLOAD, BrowserDownload))
+
+typedef struct _BrowserDownload BrowserDownload;
+typedef struct _BrowserDownloadClass BrowserDownloadClass;
+
+static GType browser_download_get_type();
+
+struct _BrowserDownloadsBar {
+ GtkInfoBar parent;
+};
+
+struct _BrowserDownloadsBarClass {
+ GtkInfoBarClass parentClass;
+};
+
+G_DEFINE_TYPE(BrowserDownloadsBar, browser_downloads_bar, GTK_TYPE_INFO_BAR)
+
+static void
+browserDownloadsBarChildRemoved(GtkContainer *infoBar, GtkWidget *widget, BrowserDownloadsBar *downloadsBar)
+{
+ GList *children = gtk_container_get_children(infoBar);
+ if (g_list_length(children) == 1)
+ gtk_info_bar_response(GTK_INFO_BAR(downloadsBar), GTK_RESPONSE_CLOSE);
+ g_list_free(children);
+}
+
+static void browserDownloadsBarResponse(GtkInfoBar *infoBar, gint responseId)
+{
+ gtk_widget_destroy(GTK_WIDGET(infoBar));
+}
+
+static void browser_downloads_bar_init(BrowserDownloadsBar *downloadsBar)
+{
+ GtkWidget *contentBox = gtk_info_bar_get_content_area(GTK_INFO_BAR(downloadsBar));
+ g_signal_connect_after(contentBox, "remove", G_CALLBACK(browserDownloadsBarChildRemoved), downloadsBar);
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(contentBox), GTK_ORIENTATION_VERTICAL);
+
+ GtkWidget *title = gtk_label_new(NULL);
+ gtk_label_set_markup(GTK_LABEL(title), "<span size='xx-large' weight='bold'>Downloads</span>");
+ gtk_misc_set_alignment(GTK_MISC(title), 0., 0.5);
+ gtk_box_pack_start(GTK_BOX(contentBox), title, FALSE, FALSE, 12);
+ gtk_widget_show(title);
+}
+
+static void browser_downloads_bar_class_init(BrowserDownloadsBarClass *klass)
+{
+ GtkInfoBarClass *infoBarClass = GTK_INFO_BAR_CLASS(klass);
+ infoBarClass->response = browserDownloadsBarResponse;
+}
+
+GtkWidget *browser_downloads_bar_new()
+{
+ GtkInfoBar *downloadsBar = GTK_INFO_BAR(g_object_new(BROWSER_TYPE_DOWNLOADS_BAR, NULL));
+ gtk_info_bar_add_buttons(downloadsBar, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
+ return GTK_WIDGET(downloadsBar);
+}
+
+struct _BrowserDownload {
+ GtkBox parent;
+
+ WebKitDownload *download;
+ guint64 contentLength;
+ guint64 downloadedSize;
+ gboolean finished;
+
+ GtkWidget *statusLabel;
+ GtkWidget *remainingLabel;
+ GtkWidget *progressBar;
+ GtkWidget *actionButton;
+};
+
+struct _BrowserDownloadClass {
+ GtkBoxClass parentClass;
+};
+
+G_DEFINE_TYPE(BrowserDownload, browser_download, GTK_TYPE_BOX)
+
+static void actionButtonClicked(GtkButton *button, BrowserDownload *browserDownload)
+{
+ if (!browserDownload->finished) {
+ webkit_download_cancel(browserDownload->download);
+ return;
+ }
+
+ gtk_show_uri(gtk_widget_get_screen(GTK_WIDGET(browserDownload)),
+ webkit_download_get_destination(browserDownload->download),
+ gtk_get_current_event_time(), NULL);
+ gtk_widget_destroy(GTK_WIDGET(browserDownload));
+}
+
+static void browserDownloadFinalize(GObject *object)
+{
+ BrowserDownload *browserDownload = BROWSER_DOWNLOAD(object);
+
+ if (browserDownload->download) {
+ g_object_unref(browserDownload->download);
+ browserDownload->download = NULL;
+ }
+
+ G_OBJECT_CLASS(browser_download_parent_class)->finalize(object);
+}
+
+static void browser_download_init(BrowserDownload *download)
+{
+ GtkWidget *mainBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start(GTK_BOX(download), mainBox, FALSE, FALSE, 0);
+ gtk_widget_show(mainBox);
+
+ GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start(GTK_BOX(mainBox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show(vbox);
+
+ GtkWidget *statusBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), statusBox, TRUE, TRUE, 0);
+ gtk_widget_show(statusBox);
+
+ download->statusLabel = gtk_label_new("Starting Download");
+ gtk_label_set_ellipsize(GTK_LABEL(download->statusLabel), PANGO_ELLIPSIZE_END);
+ gtk_misc_set_alignment(GTK_MISC(download->statusLabel), 0., 0.5);
+ gtk_box_pack_start(GTK_BOX(statusBox), download->statusLabel, TRUE, TRUE, 0);
+ gtk_widget_show(download->statusLabel);
+
+ download->remainingLabel = gtk_label_new(NULL);
+ gtk_misc_set_alignment(GTK_MISC(download->remainingLabel), 1., 0.5);
+ gtk_box_pack_end(GTK_BOX(statusBox), download->remainingLabel, TRUE, TRUE, 0);
+ gtk_widget_show(download->remainingLabel);
+
+ download->progressBar = gtk_progress_bar_new();
+ gtk_box_pack_start(GTK_BOX(vbox), download->progressBar, FALSE, FALSE, 0);
+ gtk_widget_show(download->progressBar);
+
+ download->actionButton = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(download->actionButton, "clicked", G_CALLBACK(actionButtonClicked), download);
+ gtk_box_pack_end(GTK_BOX(mainBox), download->actionButton, FALSE, FALSE, 0);
+ gtk_widget_show(download->actionButton);
+}
+
+static void browser_download_class_init(BrowserDownloadClass *klass)
+{
+ GObjectClass *objectClass = G_OBJECT_CLASS(klass);
+
+ objectClass->finalize = browserDownloadFinalize;
+}
+
+static void downloadReceivedResponse(WebKitDownload *download, GParamSpec *paramSpec, BrowserDownload *browserDownload)
+{
+ WebKitURIResponse *response = webkit_download_get_response(download);
+ browserDownload->contentLength = webkit_uri_response_get_content_length(response);
+ char *text = g_strdup_printf("Downloading %s", webkit_uri_response_get_uri(response));
+ gtk_label_set_text(GTK_LABEL(browserDownload->statusLabel), text);
+ g_free(text);
+}
+
+static gchar *remainingTime(BrowserDownload *browserDownload)
+{
+ guint64 total = browserDownload->contentLength;
+ guint64 current = browserDownload->downloadedSize;
+ gdouble elapsedTime = webkit_download_get_elapsed_time(browserDownload->download);
+
+ if (current <= 0)
+ return NULL;
+
+ gdouble perByteTime = elapsedTime / current;
+ gdouble interval = perByteTime * (total - current);
+
+ int hours = (int) (interval / 3600);
+ interval -= hours * 3600;
+ int mins = (int) (interval / 60);
+ interval -= mins * 60;
+ int secs = (int) interval;
+
+ if (hours > 0) {
+ if (mins > 0)
+ return g_strdup_printf (ngettext ("%u:%02u hour left", "%u:%02u hours left", hours), hours, mins);
+ return g_strdup_printf (ngettext ("%u hour left", "%u hours left", hours), hours);
+ }
+
+ if (mins > 0)
+ return g_strdup_printf (ngettext ("%u:%02u minute left", "%u:%02u minutes left", mins), mins, secs);
+ return g_strdup_printf (ngettext ("%u second left", "%u seconds left", secs), secs);
+}
+
+static void downloadProgress(WebKitDownload *download, GParamSpec *paramSpec, BrowserDownload *browserDownload)
+{
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(browserDownload->progressBar),
+ webkit_download_get_estimated_progress(download));
+ char *remaining = remainingTime(browserDownload);
+ gtk_label_set_text(GTK_LABEL(browserDownload->remainingLabel), remaining);
+ g_free(remaining);
+}
+
+static void downloadReceivedData(WebKitDownload *download, guint64 dataLength, BrowserDownload *browserDownload)
+{
+ browserDownload->downloadedSize += dataLength;
+}
+
+static void downloadFinished(WebKitDownload *download, BrowserDownload *browserDownload)
+{
+ gchar *text = g_strdup_printf("Download completed: %s", webkit_download_get_destination(download));
+ gtk_label_set_text(GTK_LABEL(browserDownload->statusLabel), text);
+ g_free(text);
+ gtk_label_set_text(GTK_LABEL(browserDownload->remainingLabel), NULL);
+ gtk_button_set_image(GTK_BUTTON(browserDownload->actionButton),
+ gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON));
+ gtk_button_set_label(GTK_BUTTON(browserDownload->actionButton), "Open ...");
+ browserDownload->finished = TRUE;
+}
+
+static void downloadFailed(WebKitDownload *download, GError *error, BrowserDownload *browserDownload)
+{
+ g_signal_handlers_disconnect_by_func(browserDownload->download, downloadFinished, browserDownload);
+ if (g_error_matches(error, WEBKIT_DOWNLOAD_ERROR, WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER)) {
+ gtk_widget_destroy(GTK_WIDGET(browserDownload));
+ return;
+ }
+
+ char *errorMessage = g_strdup_printf("Download failed: %s", error->message);
+ gtk_label_set_text(GTK_LABEL(browserDownload->statusLabel), errorMessage);
+ g_free(errorMessage);
+ gtk_label_set_text(GTK_LABEL(browserDownload->remainingLabel), NULL);
+ gtk_widget_set_sensitive(browserDownload->actionButton, FALSE);
+}
+
+GtkWidget *browserDownloadNew(WebKitDownload *download)
+{
+ BrowserDownload *browserDownload = BROWSER_DOWNLOAD(g_object_new(BROWSER_TYPE_DOWNLOAD,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ NULL));
+
+ browserDownload->download = g_object_ref(download);
+ g_signal_connect(browserDownload->download, "notify::response", G_CALLBACK(downloadReceivedResponse), browserDownload);
+ g_signal_connect(browserDownload->download, "notify::estimated-progress", G_CALLBACK(downloadProgress), browserDownload);
+ g_signal_connect(browserDownload->download, "received-data", G_CALLBACK(downloadReceivedData), browserDownload);
+ g_signal_connect(browserDownload->download, "finished", G_CALLBACK(downloadFinished), browserDownload);
+ g_signal_connect(browserDownload->download, "failed", G_CALLBACK(downloadFailed), browserDownload);
+
+ return GTK_WIDGET(browserDownload);
+}
+
+void browser_downloads_bar_add_download(BrowserDownloadsBar *downloadsBar, WebKitDownload *download)
+{
+ GtkWidget *browserDownload = browserDownloadNew(download);
+ GtkWidget *contentBox = gtk_info_bar_get_content_area(GTK_INFO_BAR(downloadsBar));
+ gtk_box_pack_start(GTK_BOX(contentBox), browserDownload, FALSE, TRUE, 0);
+ gtk_widget_show(browserDownload);
+}
diff --git a/Tools/MiniBrowser/gtk/BrowserDownloadsBar.h b/Tools/MiniBrowser/gtk/BrowserDownloadsBar.h
new file mode 100644
index 000000000..1704a5d01
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserDownloadsBar.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BrowserDownloadsBar_h
+#define BrowserDownloadsBar_h
+
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+G_BEGIN_DECLS
+
+#define BROWSER_TYPE_DOWNLOADS_BAR (browser_downloads_bar_get_type())
+#define BROWSER_DOWNLOADS_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BROWSER_TYPE_DOWNLOADS_BAR, BrowserDownloadsBar))
+#define BROWSER_IS_DOWNLOADS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BROWSER_TYPE_DOWNLOADS_BAR))
+#define BROWSER_DOWNLOADS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), BROWSER_TYPE_DOWNLOADS_BAR, BrowserDownloadsBarClass))
+#define BROWSER_IS_DOWNLOADS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BROWSER_TYPE_DOWNLOADS_BAR))
+#define BROWSER_DOWNLOADS_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), BROWSER_TYPE_DOWNLOADS_BAR, BrowserDownloadsBarClass))
+
+typedef struct _BrowserDownloadsBar BrowserDownloadsBar;
+typedef struct _BrowserDownloadsBarClass BrowserDownloadsBarClass;
+
+GType browser_downloads_bar_get_type(void);
+
+GtkWidget *browser_downloads_bar_new(void);
+void browser_downloads_bar_add_download(BrowserDownloadsBar *, WebKitDownload *);
+
+G_END_DECLS
+
+#endif
diff --git a/Tools/MiniBrowser/gtk/BrowserSearchBar.c b/Tools/MiniBrowser/gtk/BrowserSearchBar.c
new file mode 100644
index 000000000..22930c4c5
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserSearchBar.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cmakeconfig.h"
+#include "BrowserSearchBar.h"
+
+static const char *searchEntryFailedStyle = "GtkEntry#searchEntry {background-color: #ff6666;}";
+
+struct _BrowserSearchBar {
+ GtkToolbar parent;
+
+ WebKitWebView *webView;
+ GtkWidget *entry;
+ GtkCssProvider *cssProvider;
+ GtkWidget *prevButton;
+ GtkWidget *nextButton;
+ GtkWidget *optionsMenu;
+ GtkWidget *caseCheckButton;
+ GtkWidget *begginigWordCheckButton;
+ GtkWidget *capitalAsBegginigWordCheckButton;
+};
+
+G_DEFINE_TYPE(BrowserSearchBar, browser_search_bar, GTK_TYPE_TOOLBAR)
+
+static void setFailedStyleForEntry(BrowserSearchBar *searchBar, gboolean failedSearch)
+{
+ gtk_css_provider_load_from_data(searchBar->cssProvider, failedSearch ? searchEntryFailedStyle : "", -1, NULL);
+}
+
+static void doSearch(BrowserSearchBar *searchBar)
+{
+ GtkEntry *entry = GTK_ENTRY(searchBar->entry);
+
+ if (!gtk_entry_get_text_length(entry)) {
+ webkit_find_controller_search_finish(webkit_web_view_get_find_controller(searchBar->webView));
+ gtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+ setFailedStyleForEntry(searchBar, FALSE);
+ return;
+ }
+
+ if (!gtk_entry_get_icon_stock(entry, GTK_ENTRY_ICON_SECONDARY))
+ gtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
+
+ WebKitFindOptions options = WEBKIT_FIND_OPTIONS_WRAP_AROUND;
+ if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(searchBar->caseCheckButton)))
+ options |= WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE;
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(searchBar->begginigWordCheckButton)))
+ options |= WEBKIT_FIND_OPTIONS_AT_WORD_STARTS;
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(searchBar->capitalAsBegginigWordCheckButton)))
+ options |= WEBKIT_FIND_OPTIONS_TREAT_MEDIAL_CAPITAL_AS_WORD_START;
+
+ const gchar *text = gtk_entry_get_text(entry);
+ webkit_find_controller_search(webkit_web_view_get_find_controller(searchBar->webView), text, options, G_MAXUINT);
+}
+
+static void searchNext(BrowserSearchBar *searchBar)
+{
+ webkit_find_controller_search_next(webkit_web_view_get_find_controller(searchBar->webView));
+}
+
+static void searchPrevious(BrowserSearchBar *searchBar)
+{
+ webkit_find_controller_search_previous(webkit_web_view_get_find_controller(searchBar->webView));
+}
+
+static void searchCloseButtonClickedCallback(BrowserSearchBar *searchBar)
+{
+ browser_search_bar_close(searchBar);
+}
+
+static void searchEntryMenuIconPressedCallback(BrowserSearchBar *searchBar, GtkEntryIconPosition iconPosition, GdkEvent *event)
+{
+ if (iconPosition == GTK_ENTRY_ICON_PRIMARY) {
+ GdkEventButton *eventButton = (GdkEventButton *)event;
+ gtk_menu_popup(GTK_MENU(searchBar->optionsMenu), NULL, NULL, NULL, NULL, eventButton->button, eventButton->time);
+ }
+}
+
+static void searchEntryClearIconReleasedCallback(BrowserSearchBar *searchBar, GtkEntryIconPosition iconPosition)
+{
+ if (iconPosition == GTK_ENTRY_ICON_SECONDARY)
+ gtk_entry_set_text(GTK_ENTRY(searchBar->entry), "");
+}
+
+static void searchEntryChangedCallback(GtkEntry *entry, BrowserSearchBar *searchBar)
+{
+ doSearch(searchBar);
+}
+
+static void searchEntryActivatedCallback(BrowserSearchBar *searchBar)
+{
+ searchNext(searchBar);
+}
+
+static void searchPrevButtonClickedCallback(BrowserSearchBar *searchBar)
+{
+ searchPrevious(searchBar);
+}
+
+static void searchNextButtonClickedCallback(BrowserSearchBar *searchBar)
+{
+ searchNext(searchBar);
+}
+
+static void searchMenuCheckButtonToggledCallback(BrowserSearchBar *searchBar)
+{
+ doSearch(searchBar);
+}
+
+static void findControllerFailedToFindTextCallback(BrowserSearchBar *searchBar)
+{
+ setFailedStyleForEntry(searchBar, TRUE);
+}
+
+static void findControllerFoundTextCallback(BrowserSearchBar *searchBar)
+{
+ setFailedStyleForEntry(searchBar, FALSE);
+}
+
+static void browser_search_bar_init(BrowserSearchBar *searchBar)
+{
+ gtk_widget_set_hexpand(GTK_WIDGET(searchBar), TRUE);
+
+ GtkToolItem *toolItem = gtk_tool_item_new();
+ gtk_tool_item_set_expand(toolItem, TRUE);
+ gtk_toolbar_insert(GTK_TOOLBAR(searchBar), toolItem, 0);
+
+ GtkBox *hBox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5));
+ gtk_box_set_homogeneous(hBox, TRUE);
+ gtk_container_add(GTK_CONTAINER(toolItem), GTK_WIDGET(hBox));
+
+ gtk_box_pack_start(hBox, gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0), TRUE, TRUE, 0);
+
+ searchBar->entry = gtk_entry_new();
+ gtk_widget_set_name(searchBar->entry, "searchEntry");
+ gtk_entry_set_placeholder_text(GTK_ENTRY(searchBar->entry), "Search");
+ gtk_entry_set_icon_from_stock(GTK_ENTRY(searchBar->entry), GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
+ gtk_box_pack_start(hBox, searchBar->entry, TRUE, TRUE, 0);
+
+ searchBar->cssProvider = gtk_css_provider_new();
+ gtk_style_context_add_provider(gtk_widget_get_style_context(searchBar->entry), GTK_STYLE_PROVIDER(searchBar->cssProvider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+ GtkBox *hBoxButtons = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
+ gtk_box_pack_start(hBox, GTK_WIDGET(hBoxButtons), TRUE, TRUE, 0);
+
+ searchBar->prevButton = gtk_button_new();
+ GtkButton *button = GTK_BUTTON(searchBar->prevButton);
+ GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_button_set_image(button, image);
+ gtk_button_set_relief(button, GTK_RELIEF_NONE);
+ gtk_button_set_focus_on_click(button, FALSE);
+ gtk_box_pack_start(hBoxButtons, searchBar->prevButton, FALSE, FALSE, 0);
+
+ searchBar->nextButton = gtk_button_new();
+ button = GTK_BUTTON(searchBar->nextButton);
+ image = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_button_set_image(button, image);
+ gtk_button_set_relief(button, GTK_RELIEF_NONE);
+ gtk_button_set_focus_on_click(button, FALSE);
+ gtk_box_pack_start(hBoxButtons, searchBar->nextButton, FALSE, FALSE, 0);
+
+ GtkWidget *closeButton = gtk_button_new();
+ button = GTK_BUTTON(closeButton);
+ image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_button_set_image(button, image);
+ gtk_button_set_relief(button, GTK_RELIEF_NONE);
+ gtk_button_set_focus_on_click(button, FALSE);
+ gtk_box_pack_end(hBoxButtons, closeButton, FALSE, FALSE, 0);
+
+ searchBar->optionsMenu = g_object_ref_sink(gtk_menu_new());
+
+ searchBar->caseCheckButton = gtk_check_menu_item_new_with_mnemonic("Ca_se sensitive");
+ gtk_container_add(GTK_CONTAINER(searchBar->optionsMenu), searchBar->caseCheckButton);
+
+ searchBar->begginigWordCheckButton = gtk_check_menu_item_new_with_mnemonic("Only at the _beginning of words");
+ gtk_container_add(GTK_CONTAINER(searchBar->optionsMenu), searchBar->begginigWordCheckButton);
+
+ searchBar->capitalAsBegginigWordCheckButton = gtk_check_menu_item_new_with_mnemonic("Capital _always as beginning of word");
+ gtk_container_add(GTK_CONTAINER(searchBar->optionsMenu), searchBar->capitalAsBegginigWordCheckButton);
+
+ g_signal_connect_swapped(closeButton, "clicked", G_CALLBACK(searchCloseButtonClickedCallback), searchBar);
+ g_signal_connect_swapped(searchBar->entry, "icon-press", G_CALLBACK(searchEntryMenuIconPressedCallback), searchBar);
+ g_signal_connect_swapped(searchBar->entry, "icon-release", G_CALLBACK(searchEntryClearIconReleasedCallback), searchBar);
+ g_signal_connect_after(searchBar->entry, "changed", G_CALLBACK(searchEntryChangedCallback), searchBar);
+ g_signal_connect_swapped(searchBar->entry, "activate", G_CALLBACK(searchEntryActivatedCallback), searchBar);
+ g_signal_connect_swapped(searchBar->nextButton, "clicked", G_CALLBACK(searchNextButtonClickedCallback), searchBar);
+ g_signal_connect_swapped(searchBar->prevButton, "clicked", G_CALLBACK(searchPrevButtonClickedCallback), searchBar);
+ g_signal_connect_swapped(searchBar->caseCheckButton, "toggled", G_CALLBACK(searchMenuCheckButtonToggledCallback), searchBar);
+ g_signal_connect_swapped(searchBar->begginigWordCheckButton, "toggled", G_CALLBACK(searchMenuCheckButtonToggledCallback), searchBar);
+ g_signal_connect_swapped(searchBar->capitalAsBegginigWordCheckButton, "toggled", G_CALLBACK(searchMenuCheckButtonToggledCallback), searchBar);
+
+ gtk_widget_show_all(GTK_WIDGET(toolItem));
+ gtk_widget_show_all(searchBar->optionsMenu);
+}
+
+static void browserSearchBarFinalize(GObject *gObject)
+{
+ BrowserSearchBar *searchBar = BROWSER_SEARCH_BAR(gObject);
+
+ if (searchBar->webView) {
+ g_object_unref(searchBar->webView);
+ searchBar->webView = NULL;
+ }
+
+ if (searchBar->cssProvider) {
+ g_object_unref(searchBar->cssProvider);
+ searchBar->cssProvider = NULL;
+ }
+
+ if (searchBar->optionsMenu) {
+ g_object_unref(searchBar->optionsMenu);
+ searchBar->optionsMenu = NULL;
+ }
+
+ G_OBJECT_CLASS(browser_search_bar_parent_class)->finalize(gObject);
+}
+
+static void browser_search_bar_class_init(BrowserSearchBarClass *klass)
+{
+ GObjectClass *gObjectClass = G_OBJECT_CLASS(klass);
+
+ gObjectClass->finalize = browserSearchBarFinalize;
+}
+
+GtkWidget *browser_search_bar_new(WebKitWebView *webView)
+{
+ g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), NULL);
+
+ GtkWidget *searchBar = GTK_WIDGET(g_object_new(BROWSER_TYPE_SEARCH_BAR, NULL));
+ BROWSER_SEARCH_BAR(searchBar)->webView = g_object_ref(webView);
+
+ WebKitFindController *controller = webkit_web_view_get_find_controller(webView);
+ g_signal_connect_swapped(controller, "failed-to-find-text", G_CALLBACK(findControllerFailedToFindTextCallback), searchBar);
+ g_signal_connect_swapped(controller, "found-text", G_CALLBACK(findControllerFoundTextCallback), searchBar);
+
+ return searchBar;
+}
+
+void browser_search_bar_add_accelerators(BrowserSearchBar *searchBar, GtkAccelGroup *accelGroup)
+{
+ g_return_if_fail(BROWSER_IS_SEARCH_BAR(searchBar));
+ g_return_if_fail(GTK_IS_ACCEL_GROUP(accelGroup));
+
+ gtk_widget_add_accelerator(searchBar->nextButton, "clicked", accelGroup, GDK_KEY_F3, 0, GTK_ACCEL_VISIBLE);
+ gtk_widget_add_accelerator(searchBar->nextButton, "clicked", accelGroup, GDK_KEY_G, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
+
+ gtk_widget_add_accelerator(searchBar->prevButton, "clicked", accelGroup, GDK_KEY_F3, GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
+ gtk_widget_add_accelerator(searchBar->prevButton, "clicked", accelGroup, GDK_KEY_G, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
+}
+
+void browser_search_bar_open(BrowserSearchBar *searchBar)
+{
+ g_return_if_fail(BROWSER_IS_SEARCH_BAR(searchBar));
+
+ GtkEntry *entry = GTK_ENTRY(searchBar->entry);
+
+ gtk_widget_show(GTK_WIDGET(searchBar));
+ gtk_widget_grab_focus(GTK_WIDGET(entry));
+ gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
+ if (gtk_entry_get_text_length(entry))
+ doSearch(searchBar);
+}
+
+void browser_search_bar_close(BrowserSearchBar *searchBar)
+{
+ g_return_if_fail(BROWSER_IS_SEARCH_BAR(searchBar));
+
+ gtk_widget_hide(GTK_WIDGET(searchBar));
+ WebKitFindController *controller = webkit_web_view_get_find_controller(searchBar->webView);
+ webkit_find_controller_search_finish(controller);
+}
diff --git a/Tools/MiniBrowser/gtk/BrowserSearchBar.h b/Tools/MiniBrowser/gtk/BrowserSearchBar.h
new file mode 100644
index 000000000..1de8d8aba
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserSearchBar.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BrowserSearchBar_h
+#define BrowserSearchBar_h
+
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+G_BEGIN_DECLS
+
+#define BROWSER_TYPE_SEARCH_BAR (browser_search_bar_get_type())
+#define BROWSER_SEARCH_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BROWSER_TYPE_SEARCH_BAR, BrowserSearchBar))
+#define BROWSER_IS_SEARCH_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BROWSER_TYPE_SEARCH_BAR))
+#define BROWSER_SEARCH_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), BROWSER_TYPE_SEARCH_BAR, BrowserSearchBarClass))
+#define BROWSER_IS_SEARCH_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BROWSER_TYPE_SEARCH_BAR))
+#define BROWSER_SEARCH_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), BROWSER_TYPE_SEARCH_BAR, BrowserSearchBarClass))
+
+typedef struct _BrowserSearchBar BrowserSearchBar;
+typedef struct _BrowserSearchBarClass BrowserSearchBarClass;
+
+struct _BrowserSearchBarClass {
+ GtkToolbarClass parent_class;
+};
+
+GType browser_search_bar_get_type(void);
+
+GtkWidget *browser_search_bar_new(WebKitWebView *);
+void browser_search_bar_add_accelerators(BrowserSearchBar *, GtkAccelGroup *);
+void browser_search_bar_open(BrowserSearchBar *);
+void browser_search_bar_close(BrowserSearchBar *);
+
+G_END_DECLS
+
+#endif
diff --git a/Tools/MiniBrowser/gtk/BrowserSettingsDialog.c b/Tools/MiniBrowser/gtk/BrowserSettingsDialog.c
new file mode 100644
index 000000000..1e4dcd804
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserSettingsDialog.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "BrowserSettingsDialog.h"
+#include "BrowserCellRendererVariant.h"
+
+enum {
+ PROP_0,
+
+ PROP_SETTINGS
+};
+
+enum {
+ SETTINGS_LIST_COLUMN_NAME,
+ SETTINGS_LIST_COLUMN_NICK,
+ SETTINGS_LIST_COLUMN_BLURB,
+ SETTINGS_LIST_COLUMN_VALUE,
+ SETTINGS_LIST_COLUMN_ADJUSTMENT,
+
+ SETTINGS_LIST_N_COLUMNS
+};
+
+struct _BrowserSettingsDialog {
+ GtkDialog parent;
+
+ GtkWidget *settingsList;
+ WebKitSettings *settings;
+};
+
+struct _BrowserSettingsDialogClass {
+ GtkDialogClass parent;
+};
+
+G_DEFINE_TYPE(BrowserSettingsDialog, browser_settings_dialog, GTK_TYPE_DIALOG)
+
+static void cellRendererChanged(GtkCellRenderer *renderer, const char *path, const GValue *value, BrowserSettingsDialog *dialog)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(dialog->settingsList));
+ GtkTreePath *treePath = gtk_tree_path_new_from_string(path);
+ GtkTreeIter iter;
+ gtk_tree_model_get_iter(model, &iter, treePath);
+
+ char *name;
+ gtk_tree_model_get(model, &iter, SETTINGS_LIST_COLUMN_NAME, &name, -1);
+ g_object_set_property(G_OBJECT(dialog->settings), name, value);
+ g_free(name);
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, SETTINGS_LIST_COLUMN_VALUE, value, -1);
+ gtk_tree_path_free(treePath);
+}
+
+static void browserSettingsDialogSetProperty(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
+{
+ BrowserSettingsDialog *dialog = BROWSER_SETTINGS_DIALOG(object);
+
+ switch (propId) {
+ case PROP_SETTINGS:
+ dialog->settings = g_value_get_object(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ }
+}
+
+static void browser_settings_dialog_init(BrowserSettingsDialog *dialog)
+{
+ GtkBox *contentArea = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
+ gtk_box_set_spacing(contentArea, 2);
+
+ gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 300);
+ gtk_window_set_title(GTK_WINDOW(dialog), "WebKit View Settings");
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
+ gtk_container_set_border_width(GTK_CONTAINER(dialog), 5);
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
+
+ GtkWidget *scrolledWindow = gtk_scrolled_window_new(NULL, NULL);
+ dialog->settingsList = gtk_tree_view_new();
+ GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog->settingsList),
+ 0, "Name", renderer,
+ "text", SETTINGS_LIST_COLUMN_NICK,
+ NULL);
+ renderer = browser_cell_renderer_variant_new();
+ g_signal_connect(renderer, "changed", G_CALLBACK(cellRendererChanged), dialog);
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog->settingsList),
+ 1, "Value", renderer,
+ "value", SETTINGS_LIST_COLUMN_VALUE,
+ "adjustment", SETTINGS_LIST_COLUMN_ADJUSTMENT,
+ NULL);
+ gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(dialog->settingsList), SETTINGS_LIST_COLUMN_BLURB);
+
+ gtk_container_add(GTK_CONTAINER(scrolledWindow), dialog->settingsList);
+ gtk_widget_show(dialog->settingsList);
+
+ gtk_box_pack_start(contentArea, scrolledWindow, TRUE, TRUE, 0);
+ gtk_widget_show(scrolledWindow);
+
+ g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);
+}
+
+static void browserSettingsDialogConstructed(GObject *object)
+{
+ BrowserSettingsDialog *dialog = BROWSER_SETTINGS_DIALOG(object);
+ WebKitSettings *settings = dialog->settings;
+ GtkListStore *model = gtk_list_store_new(SETTINGS_LIST_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_VALUE, G_TYPE_OBJECT);
+ guint propertiesCount;
+ GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(settings), &propertiesCount);
+ guint i;
+ for (i = 0; i < propertiesCount; i++) {
+ GParamSpec *property = properties[i];
+ const char *name = g_param_spec_get_name(property);
+ const char *nick = g_param_spec_get_nick(property);
+
+ GValue value = { 0, { { 0 } } };
+ g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(property));
+ g_object_get_property(G_OBJECT(settings), name, &value);
+
+ GtkAdjustment *adjustment = NULL;
+ if (G_PARAM_SPEC_VALUE_TYPE(property) == G_TYPE_UINT) {
+ GParamSpecUInt *uIntProperty = G_PARAM_SPEC_UINT(property);
+ adjustment = gtk_adjustment_new(uIntProperty->default_value, uIntProperty->minimum,
+ uIntProperty->maximum, 1, 1, 1);
+ }
+
+ char *blurb = g_markup_escape_text(g_param_spec_get_blurb(property), -1);
+ GtkTreeIter iter;
+ gtk_list_store_append(model, &iter);
+ gtk_list_store_set(model, &iter,
+ SETTINGS_LIST_COLUMN_NAME, name,
+ SETTINGS_LIST_COLUMN_NICK, nick,
+ SETTINGS_LIST_COLUMN_BLURB, blurb,
+ SETTINGS_LIST_COLUMN_VALUE, &value,
+ SETTINGS_LIST_COLUMN_ADJUSTMENT, adjustment,
+ -1);
+ g_free(blurb);
+ g_value_unset(&value);
+ }
+ g_free(properties);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(dialog->settingsList), GTK_TREE_MODEL(model));
+ g_object_unref(model);
+}
+
+static void browser_settings_dialog_class_init(BrowserSettingsDialogClass *klass)
+{
+ GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
+
+ gobjectClass->constructed = browserSettingsDialogConstructed;
+ gobjectClass->set_property = browserSettingsDialogSetProperty;
+
+ g_object_class_install_property(gobjectClass,
+ PROP_SETTINGS,
+ g_param_spec_object("settings",
+ "Settings",
+ "The WebKitSettings",
+ WEBKIT_TYPE_SETTINGS,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+GtkWidget *browser_settings_dialog_new(WebKitSettings *settings)
+{
+ g_return_val_if_fail(WEBKIT_IS_SETTINGS(settings), NULL);
+
+ return GTK_WIDGET(g_object_new(BROWSER_TYPE_SETTINGS_DIALOG, "settings", settings, NULL));
+}
+
diff --git a/Tools/MiniBrowser/gtk/BrowserSettingsDialog.h b/Tools/MiniBrowser/gtk/BrowserSettingsDialog.h
new file mode 100644
index 000000000..f3dbcd042
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserSettingsDialog.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BrowserSettingsDialog_h
+#define BrowserSettingsDialog_h
+
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+G_BEGIN_DECLS
+
+#define BROWSER_TYPE_SETTINGS_DIALOG (browser_settings_dialog_get_type())
+#define BROWSER_SETTINGS_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BROWSER_TYPE_SETTINGS_DIALOG, BrowserSettingsDialog))
+#define BROWSER_SETTINGS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), BROWSER_TYPE_SETTINGS_DIALOG, BrowserSettingsDialogClass))
+#define BROWSER_IS_SETTINGS_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BROWSER_TYPE_SETTINGS_DIALOG))
+#define BROWSER_IS_SETTINGS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BROWSER_TYPE_SETTINGS_DIALOG))
+#define BROWSER_SETTINGS_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), BROWSER_TYPE_SETTINGS_DIALOG, BrowserSettingsDialogClass))
+
+typedef struct _BrowserSettingsDialog BrowserSettingsDialog;
+typedef struct _BrowserSettingsDialogClass BrowserSettingsDialogClass;
+
+GType browser_settings_dialog_get_type(void);
+
+GtkWidget* browser_settings_dialog_new(WebKitSettings *settings);
+
+G_END_DECLS
+
+#endif
diff --git a/Tools/MiniBrowser/gtk/BrowserWindow.c b/Tools/MiniBrowser/gtk/BrowserWindow.c
new file mode 100644
index 000000000..45db680f6
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserWindow.c
@@ -0,0 +1,1229 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H && defined(BUILDING_WITH_CMAKE)
+#include "cmakeconfig.h"
+#endif
+#include "BrowserWindow.h"
+
+#include "BrowserDownloadsBar.h"
+#include "BrowserSearchBar.h"
+#include "BrowserSettingsDialog.h"
+#include <gdk/gdkkeysyms.h>
+#include <string.h>
+
+enum {
+ PROP_0,
+
+ PROP_VIEW
+};
+
+struct _BrowserWindow {
+ GtkWindow parent;
+
+ GtkAccelGroup *accelGroup;
+ GtkWidget *mainBox;
+ GtkWidget *toolbar;
+ GtkWidget *uriEntry;
+ GtkWidget *backItem;
+ GtkWidget *forwardItem;
+ GtkWidget *zoomInItem;
+ GtkWidget *zoomOutItem;
+ GtkWidget *boldItem;
+ GtkWidget *italicItem;
+ GtkWidget *underlineItem;
+ GtkWidget *strikethroughItem;
+ GtkWidget *statusLabel;
+ GtkWidget *settingsDialog;
+ WebKitWebView *webView;
+ GtkWidget *downloadsBar;
+ BrowserSearchBar *searchBar;
+ gboolean searchBarVisible;
+ gboolean inspectorWindowIsVisible;
+ gboolean fullScreenIsEnabled;
+ GdkPixbuf *favicon;
+ GtkWidget *reloadOrStopButton;
+ GtkWidget *fullScreenMessageLabel;
+ GtkWindow *parentWindow;
+ guint fullScreenMessageLabelId;
+ guint resetEntryProgressTimeoutId;
+};
+
+struct _BrowserWindowClass {
+ GtkWindowClass parent;
+};
+
+static const char *defaultWindowTitle = "WebKitGTK+ MiniBrowser";
+static const char *miniBrowserAboutScheme = "minibrowser-about";
+static const gdouble minimumZoomLevel = 0.5;
+static const gdouble maximumZoomLevel = 3;
+static const gdouble defaultZoomLevel = 1;
+static const gdouble zoomStep = 1.2;
+static gint windowCount = 0;
+
+G_DEFINE_TYPE(BrowserWindow, browser_window, GTK_TYPE_WINDOW)
+
+static char *getInternalURI(const char *uri)
+{
+ // Internally we use minibrowser-about: as about: prefix is ignored by WebKit.
+ if (g_str_has_prefix(uri, "about:") && !g_str_equal(uri, "about:blank"))
+ return g_strconcat(miniBrowserAboutScheme, uri + strlen ("about"), NULL);
+
+ return g_strdup(uri);
+}
+
+static char *getExternalURI(const char *uri)
+{
+ // From the user point of view we support about: prefix.
+ if (g_str_has_prefix(uri, miniBrowserAboutScheme))
+ return g_strconcat("about", uri + strlen(miniBrowserAboutScheme), NULL);
+
+ return g_strdup(uri);
+}
+
+static void browserWindowSetStatusText(BrowserWindow *window, const char *text)
+{
+ gtk_label_set_text(GTK_LABEL(window->statusLabel), text);
+ gtk_widget_set_visible(window->statusLabel, !!text);
+}
+
+static void resetStatusText(GtkWidget *widget, BrowserWindow *window)
+{
+ browserWindowSetStatusText(window, NULL);
+}
+
+static void activateUriEntryCallback(BrowserWindow *window)
+{
+ browser_window_load_uri(window, gtk_entry_get_text(GTK_ENTRY(window->uriEntry)));
+}
+
+static void reloadOrStopCallback(BrowserWindow *window)
+{
+ if (webkit_web_view_is_loading(window->webView))
+ webkit_web_view_stop_loading(window->webView);
+ else
+ webkit_web_view_reload(window->webView);
+}
+
+static void goBackCallback(BrowserWindow *window)
+{
+ webkit_web_view_go_back(window->webView);
+}
+
+static void goForwardCallback(BrowserWindow *window)
+{
+ webkit_web_view_go_forward(window->webView);
+}
+
+static void settingsCallback(BrowserWindow *window)
+{
+ if (window->settingsDialog) {
+ gtk_window_present(GTK_WINDOW(window->settingsDialog));
+ return;
+ }
+
+ window->settingsDialog = browser_settings_dialog_new(webkit_web_view_get_settings(window->webView));
+ gtk_window_set_transient_for(GTK_WINDOW(window->settingsDialog), GTK_WINDOW(window));
+ g_object_add_weak_pointer(G_OBJECT(window->settingsDialog), (gpointer *)&window->settingsDialog);
+ gtk_widget_show(window->settingsDialog);
+}
+
+static void webViewURIChanged(WebKitWebView *webView, GParamSpec *pspec, BrowserWindow *window)
+{
+ char *externalURI = getExternalURI(webkit_web_view_get_uri(webView));
+ gtk_entry_set_text(GTK_ENTRY(window->uriEntry), externalURI);
+ g_free(externalURI);
+}
+
+static void webViewTitleChanged(WebKitWebView *webView, GParamSpec *pspec, BrowserWindow *window)
+{
+ const char *title = webkit_web_view_get_title(webView);
+ gtk_window_set_title(GTK_WINDOW(window), title ? title : defaultWindowTitle);
+}
+
+static gboolean resetEntryProgress(BrowserWindow *window)
+{
+ gtk_entry_set_progress_fraction(GTK_ENTRY(window->uriEntry), 0);
+ window->resetEntryProgressTimeoutId = 0;
+ return FALSE;
+}
+
+static void webViewLoadProgressChanged(WebKitWebView *webView, GParamSpec *pspec, BrowserWindow *window)
+{
+ gdouble progress = webkit_web_view_get_estimated_load_progress(webView);
+ gtk_entry_set_progress_fraction(GTK_ENTRY(window->uriEntry), progress);
+ if (progress == 1.0) {
+ window->resetEntryProgressTimeoutId = g_timeout_add(500, (GSourceFunc)resetEntryProgress, window);
+ g_source_set_name_by_id(window->resetEntryProgressTimeoutId, "[WebKit] resetEntryProgress");
+ }
+}
+
+static void downloadStarted(WebKitWebContext *webContext, WebKitDownload *download, BrowserWindow *window)
+{
+ if (!window->downloadsBar) {
+ window->downloadsBar = browser_downloads_bar_new();
+ gtk_box_pack_start(GTK_BOX(window->mainBox), window->downloadsBar, FALSE, FALSE, 0);
+ gtk_box_reorder_child(GTK_BOX(window->mainBox), window->downloadsBar, 0);
+ g_object_add_weak_pointer(G_OBJECT(window->downloadsBar), (gpointer *)&(window->downloadsBar));
+ gtk_widget_show(window->downloadsBar);
+ }
+ browser_downloads_bar_add_download(BROWSER_DOWNLOADS_BAR(window->downloadsBar), download);
+}
+
+static void browserWindowHistoryItemSelected(BrowserWindow *window, GtkMenuItem *item)
+{
+ GtkAction *action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(item));
+ browserWindowSetStatusText(window, action ? gtk_action_get_name(action) : NULL);
+}
+
+static void browserWindowHistoryItemActivated(BrowserWindow *window, GtkAction *action)
+{
+ WebKitBackForwardListItem *item = g_object_get_data(G_OBJECT(action), "back-forward-list-item");
+ if (!item)
+ return;
+
+ webkit_web_view_go_to_back_forward_list_item(window->webView, item);
+}
+
+static GtkWidget *browserWindowCreateBackForwardMenu(BrowserWindow *window, GList *list)
+{
+ if (!list)
+ return NULL;
+
+ GtkWidget *menu = gtk_menu_new();
+ GList *listItem;
+ for (listItem = list; listItem; listItem = g_list_next(listItem)) {
+ WebKitBackForwardListItem *item = (WebKitBackForwardListItem *)listItem->data;
+ const char *uri = webkit_back_forward_list_item_get_uri(item);
+ const char *title = webkit_back_forward_list_item_get_title(item);
+
+ GtkAction *action = gtk_action_new(uri, title, NULL, NULL);
+ g_object_set_data_full(G_OBJECT(action), "back-forward-list-item", g_object_ref(item), g_object_unref);
+ g_signal_connect_swapped(action, "activate", G_CALLBACK(browserWindowHistoryItemActivated), window);
+
+ GtkWidget *menuItem = gtk_action_create_menu_item(action);
+ g_signal_connect_swapped(menuItem, "select", G_CALLBACK(browserWindowHistoryItemSelected), window);
+ g_object_unref(action);
+
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuItem);
+ gtk_widget_show(menuItem);
+ }
+
+ g_signal_connect(menu, "hide", G_CALLBACK(resetStatusText), window);
+
+ return menu;
+}
+
+static void browserWindowUpdateNavigationActions(BrowserWindow *window, WebKitBackForwardList *backForwadlist)
+{
+ gtk_widget_set_sensitive(window->backItem, webkit_web_view_can_go_back(window->webView));
+ gtk_widget_set_sensitive(window->forwardItem, webkit_web_view_can_go_forward(window->webView));
+
+ GList *list = g_list_reverse(webkit_back_forward_list_get_back_list_with_limit(backForwadlist, 10));
+ gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(window->backItem),
+ browserWindowCreateBackForwardMenu(window, list));
+ g_list_free(list);
+
+ list = webkit_back_forward_list_get_forward_list_with_limit(backForwadlist, 10);
+ gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(window->forwardItem),
+ browserWindowCreateBackForwardMenu(window, list));
+ g_list_free(list);
+}
+
+static void backForwadlistChanged(WebKitBackForwardList *backForwadlist, WebKitBackForwardListItem *itemAdded, GList *itemsRemoved, BrowserWindow *window)
+{
+ browserWindowUpdateNavigationActions(window, backForwadlist);
+}
+
+static void permissionRequestDialogCallback(GtkDialog *dialog, gint response, WebKitPermissionRequest *request)
+{
+ switch (response) {
+ case GTK_RESPONSE_YES:
+ webkit_permission_request_allow(request);
+ break;
+ default:
+ webkit_permission_request_deny(request);
+ break;
+ }
+
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ g_object_unref(request);
+}
+
+static void webViewClose(WebKitWebView *webView, BrowserWindow *window)
+{
+ gtk_widget_destroy(GTK_WIDGET(window));
+}
+
+static void webViewRunAsModal(WebKitWebView *webView, BrowserWindow *window)
+{
+ gtk_window_set_modal(GTK_WINDOW(window), TRUE);
+ gtk_window_set_transient_for(GTK_WINDOW(window), window->parentWindow);
+}
+
+static void webViewReadyToShow(WebKitWebView *webView, BrowserWindow *window)
+{
+ WebKitWindowProperties *windowProperties = webkit_web_view_get_window_properties(webView);
+
+ GdkRectangle geometry;
+ webkit_window_properties_get_geometry(windowProperties, &geometry);
+ if (geometry.x >= 0 && geometry.y >= 0)
+ gtk_window_move(GTK_WINDOW(window), geometry.x, geometry.y);
+ if (geometry.width > 0 && geometry.height > 0)
+ gtk_window_resize(GTK_WINDOW(window), geometry.width, geometry.height);
+
+ if (!webkit_window_properties_get_toolbar_visible(windowProperties))
+ gtk_widget_hide(window->toolbar);
+ else if (!webkit_window_properties_get_locationbar_visible(windowProperties))
+ gtk_widget_hide(window->uriEntry);
+
+ if (!webkit_window_properties_get_resizable(windowProperties))
+ gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
+
+ gtk_widget_show(GTK_WIDGET(window));
+}
+
+static gboolean fullScreenMessageTimeoutCallback(BrowserWindow *window)
+{
+ gtk_widget_hide(window->fullScreenMessageLabel);
+ window->fullScreenMessageLabelId = 0;
+ return FALSE;
+}
+
+static gboolean webViewEnterFullScreen(WebKitWebView *webView, BrowserWindow *window)
+{
+ gchar *titleOrURI = g_strdup(webkit_web_view_get_title(window->webView));
+ if (!titleOrURI)
+ titleOrURI = getExternalURI(webkit_web_view_get_uri(window->webView));
+ gchar *message = g_strdup_printf("%s is now full screen. Press ESC or f to exit.", titleOrURI);
+ gtk_label_set_text(GTK_LABEL(window->fullScreenMessageLabel), message);
+ g_free(titleOrURI);
+ g_free(message);
+
+ gtk_widget_show(window->fullScreenMessageLabel);
+
+ window->fullScreenMessageLabelId = g_timeout_add_seconds(2, (GSourceFunc)fullScreenMessageTimeoutCallback, window);
+ g_source_set_name_by_id(window->fullScreenMessageLabelId, "[WebKit] fullScreenMessageTimeoutCallback");
+ gtk_widget_hide(window->toolbar);
+ window->searchBarVisible = gtk_widget_get_visible(GTK_WIDGET(window->searchBar));
+ browser_search_bar_close(window->searchBar);
+
+ return FALSE;
+}
+
+static gboolean webViewLeaveFullScreen(WebKitWebView *webView, BrowserWindow *window)
+{
+ if (window->fullScreenMessageLabelId) {
+ g_source_remove(window->fullScreenMessageLabelId);
+ window->fullScreenMessageLabelId = 0;
+ }
+ gtk_widget_hide(window->fullScreenMessageLabel);
+ gtk_widget_show(window->toolbar);
+ if (window->searchBarVisible) {
+ // Opening the search bar steals the focus. Usually, we want
+ // this but not when coming back from fullscreen.
+ GtkWidget *focusWidget = gtk_window_get_focus(GTK_WINDOW(window));
+ browser_search_bar_open(window->searchBar);
+ gtk_window_set_focus(GTK_WINDOW(window), focusWidget);
+ }
+
+ return FALSE;
+}
+
+static GtkWidget *webViewCreate(WebKitWebView *webView, WebKitNavigationAction *navigation, BrowserWindow *window)
+{
+ WebKitWebView *newWebView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(webView));
+ webkit_web_view_set_settings(newWebView, webkit_web_view_get_settings(webView));
+
+ GtkWidget *newWindow = browser_window_new(newWebView, GTK_WINDOW(window));
+ g_signal_connect(newWebView, "ready-to-show", G_CALLBACK(webViewReadyToShow), newWindow);
+ g_signal_connect(newWebView, "run-as-modal", G_CALLBACK(webViewRunAsModal), newWindow);
+ g_signal_connect(newWebView, "close", G_CALLBACK(webViewClose), newWindow);
+ return GTK_WIDGET(newWebView);
+}
+
+static gboolean webViewLoadFailed(WebKitWebView *webView, WebKitLoadEvent loadEvent, const char *failingURI, GError *error, BrowserWindow *window)
+{
+ gtk_entry_set_progress_fraction(GTK_ENTRY(window->uriEntry), 0.);
+ return FALSE;
+}
+
+static gboolean webViewLoadFailedWithTLSerrors(WebKitWebView *webView, const char *failingURI, GTlsCertificate *certificate, GTlsCertificateFlags errors, BrowserWindow *window)
+{
+ GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", "Invalid TLS Certificate");
+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "Failed to load %s: Do you want to continue ignoring the TLS errors?", failingURI);
+ int response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ if (response == GTK_RESPONSE_YES) {
+ SoupURI *uri = soup_uri_new(failingURI);
+ webkit_web_context_allow_tls_certificate_for_host(webkit_web_view_get_context(webView), certificate, uri->host);
+ soup_uri_free(uri);
+ webkit_web_view_load_uri(webView, failingURI);
+ }
+
+ return TRUE;
+}
+
+static gboolean webViewDecidePolicy(WebKitWebView *webView, WebKitPolicyDecision *decision, WebKitPolicyDecisionType decisionType, BrowserWindow *window)
+{
+ switch (decisionType) {
+ case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION: {
+ WebKitNavigationAction *navigationAction = webkit_navigation_policy_decision_get_navigation_action(WEBKIT_NAVIGATION_POLICY_DECISION(decision));
+ if (webkit_navigation_action_get_navigation_type(navigationAction) != WEBKIT_NAVIGATION_TYPE_LINK_CLICKED
+ || webkit_navigation_action_get_mouse_button(navigationAction) != GDK_BUTTON_MIDDLE)
+ return FALSE;
+
+ // Opening a new window if link clicked with the middle button.
+ WebKitWebView *newWebView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(webkit_web_view_get_context(webView)));
+ GtkWidget *newWindow = browser_window_new(newWebView, GTK_WINDOW(window));
+ webkit_web_view_load_request(newWebView, webkit_navigation_action_get_request(navigationAction));
+ gtk_widget_show(newWindow);
+
+ webkit_policy_decision_ignore(decision);
+ return TRUE;
+ }
+ case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: {
+ WebKitResponsePolicyDecision *responseDecision = WEBKIT_RESPONSE_POLICY_DECISION(decision);
+ if (webkit_response_policy_decision_is_mime_type_supported(responseDecision))
+ return FALSE;
+
+ WebKitWebResource *mainResource = webkit_web_view_get_main_resource(webView);
+ WebKitURIRequest *request = webkit_response_policy_decision_get_request(responseDecision);
+ const char *requestURI = webkit_uri_request_get_uri(request);
+ if (g_strcmp0(webkit_web_resource_get_uri(mainResource), requestURI))
+ return FALSE;
+
+ webkit_policy_decision_download(decision);
+ return TRUE;
+ }
+ case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
+ default:
+ return FALSE;
+ }
+}
+
+static gboolean webViewDecidePermissionRequest(WebKitWebView *webView, WebKitPermissionRequest *request, BrowserWindow *window)
+{
+ const gchar *dialog_title = NULL;
+ const gchar *dialog_message = NULL;
+ const gchar *dialog_message_format = NULL;
+
+ if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(request)) {
+ dialog_title = "Geolocation request";
+ dialog_message_format = "%s";
+ dialog_message = "Allow geolocation request?";
+ } else if (WEBKIT_IS_NOTIFICATION_PERMISSION_REQUEST(request)) {
+ dialog_title = "Notification request";
+ dialog_message_format = "%s";
+ dialog_message = "Allow notifications request?";
+ } else if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(request)) {
+ dialog_message_format = "Allow access to %s device?";
+ gboolean is_for_audio_device = webkit_user_media_permission_is_for_audio_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request));
+ gboolean is_for_video_device = webkit_user_media_permission_is_for_video_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request));
+ dialog_title = "UserMedia request";
+ if (is_for_audio_device) {
+ if (is_for_video_device)
+ dialog_message = "audio/video";
+ else
+ dialog_message = "audio";
+ } else if (is_for_video_device)
+ dialog_message = "video";
+ } else if (WEBKIT_IS_INSTALL_MISSING_MEDIA_PLUGINS_PERMISSION_REQUEST(request)) {
+ dialog_title = "Media plugin missing request";
+ dialog_message_format = "The media backend was unable to find a plugin to play the requested media:\n%s.\nAllow to search and install the missing plugin?";
+ dialog_message = webkit_install_missing_media_plugins_permission_request_get_description(WEBKIT_INSTALL_MISSING_MEDIA_PLUGINS_PERMISSION_REQUEST(request));
+ } else
+ return FALSE;
+
+ GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_YES_NO,
+ "%s", dialog_title);
+
+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), dialog_message_format, dialog_message);
+ g_signal_connect(dialog, "response", G_CALLBACK(permissionRequestDialogCallback), g_object_ref(request));
+ gtk_widget_show(dialog);
+ return TRUE;
+}
+
+static void webViewMouseTargetChanged(WebKitWebView *webView, WebKitHitTestResult *hitTestResult, guint mouseModifiers, BrowserWindow *window)
+{
+ if (!webkit_hit_test_result_context_is_link(hitTestResult)) {
+ browserWindowSetStatusText(window, NULL);
+ return;
+ }
+ browserWindowSetStatusText(window, webkit_hit_test_result_get_link_uri(hitTestResult));
+}
+
+static gboolean browserWindowCanZoomIn(BrowserWindow *window)
+{
+ gdouble zoomLevel = webkit_web_view_get_zoom_level(window->webView) * zoomStep;
+ return zoomLevel < maximumZoomLevel;
+}
+
+static gboolean browserWindowCanZoomOut(BrowserWindow *window)
+{
+ gdouble zoomLevel = webkit_web_view_get_zoom_level(window->webView) / zoomStep;
+ return zoomLevel > minimumZoomLevel;
+}
+
+static gboolean browserWindowZoomIn(BrowserWindow *window)
+{
+ if (browserWindowCanZoomIn(window)) {
+ gdouble zoomLevel = webkit_web_view_get_zoom_level(window->webView) * zoomStep;
+ webkit_web_view_set_zoom_level(window->webView, zoomLevel);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean browserWindowZoomOut(BrowserWindow *window)
+{
+ if (browserWindowCanZoomOut(window)) {
+ gdouble zoomLevel = webkit_web_view_get_zoom_level(window->webView) / zoomStep;
+ webkit_web_view_set_zoom_level(window->webView, zoomLevel);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean scrollEventCallback(WebKitWebView *webView, const GdkEventScroll *event, BrowserWindow *window)
+{
+ GdkModifierType mod = gtk_accelerator_get_default_mod_mask();
+
+ if ((event->state & mod) != GDK_CONTROL_MASK)
+ return FALSE;
+
+ if (event->delta_y < 0)
+ return browserWindowZoomIn(window);
+
+ return browserWindowZoomOut(window);
+}
+
+#if GTK_CHECK_VERSION(3, 12, 0)
+static void colorChooserRGBAChanged(GtkColorChooser *colorChooser, GParamSpec *paramSpec, WebKitColorChooserRequest *request)
+{
+ GdkRGBA rgba;
+ gtk_color_chooser_get_rgba(colorChooser, &rgba);
+ webkit_color_chooser_request_set_rgba(request, &rgba);
+}
+
+static void popoverColorClosed(GtkWidget *popover, WebKitColorChooserRequest *request)
+{
+ webkit_color_chooser_request_finish(request);
+}
+
+static void colorChooserRequestFinished(WebKitColorChooserRequest *request, GtkWidget *popover)
+{
+ g_object_unref(request);
+ gtk_widget_destroy(popover);
+}
+
+static gboolean runColorChooserCallback(WebKitWebView *webView, WebKitColorChooserRequest *request, BrowserWindow *window)
+{
+ GtkWidget *popover = gtk_popover_new(GTK_WIDGET(webView));
+
+ GdkRectangle rectangle;
+ webkit_color_chooser_request_get_element_rectangle(request, &rectangle);
+ gtk_popover_set_pointing_to(GTK_POPOVER(popover), &rectangle);
+
+ GtkWidget *colorChooser = gtk_color_chooser_widget_new();
+ GdkRGBA rgba;
+ webkit_color_chooser_request_get_rgba(request, &rgba);
+ gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(colorChooser), &rgba);
+ g_signal_connect(colorChooser, "notify::rgba", G_CALLBACK(colorChooserRGBAChanged), request);
+ gtk_container_add(GTK_CONTAINER(popover), colorChooser);
+ gtk_widget_show(colorChooser);
+
+ g_object_ref(request);
+ g_signal_connect_object(popover, "hide", G_CALLBACK(popoverColorClosed), request, 0);
+ g_signal_connect_object(request, "finished", G_CALLBACK(colorChooserRequestFinished), popover, 0);
+
+ gtk_widget_show(popover);
+
+ return TRUE;
+}
+#endif /* GTK_CHECK_VERSION(3, 12, 0) */
+
+static void browserWindowUpdateZoomActions(BrowserWindow *window)
+{
+ gtk_widget_set_sensitive(window->zoomInItem, browserWindowCanZoomIn(window));
+ gtk_widget_set_sensitive(window->zoomOutItem, browserWindowCanZoomOut(window));
+}
+
+static void webViewZoomLevelChanged(GObject *object, GParamSpec *paramSpec, BrowserWindow *window)
+{
+ browserWindowUpdateZoomActions(window);
+}
+
+static void updateUriEntryIcon(BrowserWindow *window)
+{
+ GtkEntry *entry = GTK_ENTRY(window->uriEntry);
+ if (window->favicon)
+ gtk_entry_set_icon_from_pixbuf(entry, GTK_ENTRY_ICON_PRIMARY, window->favicon);
+ else
+ gtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_NEW);
+}
+
+static void faviconChanged(GObject *object, GParamSpec *paramSpec, BrowserWindow *window)
+{
+ GdkPixbuf *favicon = NULL;
+ cairo_surface_t *surface = webkit_web_view_get_favicon(window->webView);
+
+ if (surface) {
+ int width = cairo_image_surface_get_width(surface);
+ int height = cairo_image_surface_get_height(surface);
+ favicon = gdk_pixbuf_get_from_surface(surface, 0, 0, width, height);
+ }
+
+ if (window->favicon)
+ g_object_unref(window->favicon);
+ window->favicon = favicon;
+
+ updateUriEntryIcon(window);
+}
+
+static void webViewIsLoadingChanged(GObject *object, GParamSpec *paramSpec, BrowserWindow *window)
+{
+ gboolean isLoading = webkit_web_view_is_loading(window->webView);
+ gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(window->reloadOrStopButton), isLoading ? GTK_STOCK_STOP : GTK_STOCK_REFRESH);
+}
+
+static gboolean inspectorWasOpenedInAnotherWindow(WebKitWebInspector *inspectorWindow, BrowserWindow *window)
+{
+ window->inspectorWindowIsVisible = TRUE;
+ return FALSE;
+}
+
+static gboolean inspectorWasClosed(WebKitWebInspector *inspectorWindow, BrowserWindow *window)
+{
+ window->inspectorWindowIsVisible = FALSE;
+ return FALSE;
+}
+
+static void zoomInCallback(BrowserWindow *window)
+{
+ browserWindowZoomIn(window);
+}
+
+static void zoomOutCallback(BrowserWindow *window)
+{
+ browserWindowZoomOut(window);
+}
+
+static void defaultZoomCallback(BrowserWindow *window)
+{
+ webkit_web_view_set_zoom_level(window->webView, defaultZoomLevel);
+}
+
+static void searchCallback(BrowserWindow *window)
+{
+ browser_search_bar_open(window->searchBar);
+}
+
+static gboolean toggleWebInspector(BrowserWindow *window, gpointer user_data)
+{
+ WebKitWebInspector *inspectorWindow;
+
+ inspectorWindow = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(window->webView));
+ if (!window->inspectorWindowIsVisible) {
+ webkit_web_inspector_show(inspectorWindow);
+ window->inspectorWindowIsVisible = TRUE;
+ } else
+ webkit_web_inspector_close(inspectorWindow);
+
+ return TRUE;
+}
+
+static void reloadPage(BrowserWindow *window, gpointer user_data)
+{
+ webkit_web_view_reload(window->webView);
+}
+
+static void reloadPageIgnoringCache(BrowserWindow *window, gpointer user_data)
+{
+ webkit_web_view_reload_bypass_cache(window->webView);
+}
+
+static void stopPageLoad(BrowserWindow *window, gpointer user_data)
+{
+ if (gtk_widget_get_visible(GTK_WIDGET(window->searchBar)))
+ browser_search_bar_close(window->searchBar);
+ else if (webkit_web_view_is_loading(window->webView))
+ webkit_web_view_stop_loading(window->webView);
+}
+
+static void loadHomePage(BrowserWindow *window, gpointer user_data)
+{
+ webkit_web_view_load_uri(window->webView, BROWSER_DEFAULT_URL);
+}
+
+static gboolean toggleFullScreen(BrowserWindow *window, gpointer user_data)
+{
+ if (!window->fullScreenIsEnabled) {
+ gtk_window_fullscreen(GTK_WINDOW(window));
+ gtk_widget_hide(window->toolbar);
+ window->fullScreenIsEnabled = TRUE;
+ } else {
+ gtk_window_unfullscreen(GTK_WINDOW(window));
+ gtk_widget_show(window->toolbar);
+ window->fullScreenIsEnabled = FALSE;
+ }
+ return TRUE;
+}
+
+static void editingCommandCallback(GtkWidget*widget, BrowserWindow *window)
+{
+ webkit_web_view_execute_editing_command(window->webView, gtk_widget_get_name(widget));
+}
+
+static void insertImageCommandCallback(GtkWidget*widget, BrowserWindow *window)
+{
+ GtkWidget *fileChooser = gtk_file_chooser_dialog_new("Insert Image", GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_OPEN,
+ "Cancel", GTK_RESPONSE_CANCEL, "Open", GTK_RESPONSE_ACCEPT, NULL);
+
+ GtkFileFilter *filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, "Images");
+ gtk_file_filter_add_pixbuf_formats(filter);
+ gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fileChooser), filter);
+
+ if (gtk_dialog_run(GTK_DIALOG(fileChooser)) == GTK_RESPONSE_ACCEPT) {
+ char *uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(fileChooser));
+ if (uri) {
+ webkit_web_view_execute_editing_command_with_argument(window->webView, WEBKIT_EDITING_COMMAND_INSERT_IMAGE, uri);
+ g_free(uri);
+ }
+ }
+
+ gtk_widget_destroy(fileChooser);
+}
+
+static void insertLinkCommandCallback(GtkWidget*widget, BrowserWindow *window)
+{
+ GtkWidget *dialog = gtk_dialog_new_with_buttons("Insert Link", GTK_WINDOW(window), GTK_DIALOG_MODAL, "Insert", GTK_RESPONSE_ACCEPT, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+ GtkWidget *entry = gtk_entry_new();
+ gtk_entry_set_placeholder_text(GTK_ENTRY(entry), "URL");
+ gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), entry);
+ gtk_widget_show(entry);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+ const char *url = gtk_entry_get_text(GTK_ENTRY(entry));
+ if (url && *url)
+ webkit_web_view_execute_editing_command_with_argument(window->webView, WEBKIT_EDITING_COMMAND_CREATE_LINK, url);
+ }
+
+ gtk_widget_destroy(dialog);
+}
+
+static void browserWindowEditingCommandToggleButtonSetActive(BrowserWindow *window, GtkWidget *button, gboolean active)
+{
+ g_signal_handlers_block_by_func(button, G_CALLBACK(editingCommandCallback), window);
+ gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(button), active);
+ g_signal_handlers_unblock_by_func(button, G_CALLBACK(editingCommandCallback), window);
+}
+
+static void typingAttributesChanged(WebKitEditorState *editorState, GParamSpec *spec, BrowserWindow *window)
+{
+ unsigned typingAttributes = webkit_editor_state_get_typing_attributes(editorState);
+ browserWindowEditingCommandToggleButtonSetActive(window, window->boldItem, typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD);
+ browserWindowEditingCommandToggleButtonSetActive(window, window->italicItem, typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC);
+ browserWindowEditingCommandToggleButtonSetActive(window, window->underlineItem, typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE);
+ browserWindowEditingCommandToggleButtonSetActive(window, window->strikethroughItem, typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH);
+}
+
+static void browserWindowFinalize(GObject *gObject)
+{
+ BrowserWindow *window = BROWSER_WINDOW(gObject);
+ if (window->favicon) {
+ g_object_unref(window->favicon);
+ window->favicon = NULL;
+ }
+
+ if (window->accelGroup) {
+ g_object_unref(window->accelGroup);
+ window->accelGroup = NULL;
+ }
+
+ if (window->fullScreenMessageLabelId)
+ g_source_remove(window->fullScreenMessageLabelId);
+
+ if (window->resetEntryProgressTimeoutId)
+ g_source_remove(window->resetEntryProgressTimeoutId);
+
+ G_OBJECT_CLASS(browser_window_parent_class)->finalize(gObject);
+
+ if (g_atomic_int_dec_and_test(&windowCount))
+ gtk_main_quit();
+}
+
+static void browserWindowGetProperty(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
+{
+ BrowserWindow *window = BROWSER_WINDOW(object);
+
+ switch (propId) {
+ case PROP_VIEW:
+ g_value_set_object(value, browser_window_get_view(window));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ }
+}
+
+static void browserWindowSetProperty(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
+{
+ BrowserWindow* window = BROWSER_WINDOW(object);
+
+ switch (propId) {
+ case PROP_VIEW:
+ window->webView = g_value_get_object(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ }
+}
+
+static void browserWindowSetupEditorToolbar(BrowserWindow *window)
+{
+ GtkWidget *toolbar = gtk_toolbar_new();
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_HORIZONTAL);
+ gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ);
+
+ GtkToolItem *item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_BOLD);
+ window->boldItem = GTK_WIDGET(item);
+ gtk_widget_set_name(GTK_WIDGET(item), "Bold");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_ITALIC);
+ window->italicItem = GTK_WIDGET(item);
+ gtk_widget_set_name(GTK_WIDGET(item), "Italic");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_UNDERLINE);
+ window->underlineItem = GTK_WIDGET(item);
+ gtk_widget_set_name(GTK_WIDGET(item), "Underline");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_STRIKETHROUGH);
+ gtk_widget_set_name(GTK_WIDGET(item), "Strikethrough");
+ window->strikethroughItem = GTK_WIDGET(item);
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_CUT);
+ gtk_widget_set_name(GTK_WIDGET(item), WEBKIT_EDITING_COMMAND_CUT);
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_COPY);
+ gtk_widget_set_name(GTK_WIDGET(item), WEBKIT_EDITING_COMMAND_COPY);
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_PASTE);
+ gtk_widget_set_name(GTK_WIDGET(item), WEBKIT_EDITING_COMMAND_PASTE);
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_UNDO);
+ gtk_widget_set_name(GTK_WIDGET(item), WEBKIT_EDITING_COMMAND_UNDO);
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_REDO);
+ gtk_widget_set_name(GTK_WIDGET(item), WEBKIT_EDITING_COMMAND_REDO);
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_radio_tool_button_new_from_stock(NULL, GTK_STOCK_JUSTIFY_LEFT);
+ GSList *justifyRadioGroup = gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(item));
+ gtk_widget_set_name(GTK_WIDGET(item), "JustifyLeft");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_radio_tool_button_new_from_stock(justifyRadioGroup, GTK_STOCK_JUSTIFY_CENTER);
+ justifyRadioGroup = gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(item));
+ gtk_widget_set_name(GTK_WIDGET(item), "JustifyCenter");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_radio_tool_button_new_from_stock(justifyRadioGroup, GTK_STOCK_JUSTIFY_RIGHT);
+ gtk_widget_set_name(GTK_WIDGET(item), "JustifyRight");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_INDENT);
+ gtk_widget_set_name(GTK_WIDGET(item), "Indent");
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_UNINDENT);
+ gtk_widget_set_name(GTK_WIDGET(item), "Outdent");
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new(NULL, NULL);
+ gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "insert-image");
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(insertImageCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new(NULL, NULL);
+ gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "insert-link");
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(insertLinkCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ gtk_box_pack_start(GTK_BOX(window->mainBox), toolbar, FALSE, FALSE, 0);
+ gtk_widget_show(toolbar);
+}
+
+static void browser_window_init(BrowserWindow *window)
+{
+ g_atomic_int_inc(&windowCount);
+
+ gtk_window_set_title(GTK_WINDOW(window), defaultWindowTitle);
+ gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
+
+ window->uriEntry = gtk_entry_new();
+ g_signal_connect_swapped(window->uriEntry, "activate", G_CALLBACK(activateUriEntryCallback), (gpointer)window);
+ gtk_entry_set_icon_activatable(GTK_ENTRY(window->uriEntry), GTK_ENTRY_ICON_PRIMARY, FALSE);
+ updateUriEntryIcon(window);
+
+ /* Keyboard accelerators */
+ window->accelGroup = gtk_accel_group_new();
+ gtk_window_add_accel_group(GTK_WINDOW(window), window->accelGroup);
+
+ /* Global accelerators */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_I, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(toggleWebInspector), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_F12, 0, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(toggleWebInspector), window, NULL));
+
+ /* Reload page */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_F5, 0, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(reloadPage), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_R, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(reloadPage), window, NULL));
+
+ /* Reload page ignoring cache */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_F5, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(reloadPageIgnoringCache), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_R, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(reloadPageIgnoringCache), window, NULL));
+
+ /* Stop page load */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_F6, 0, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(stopPageLoad), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_Escape, 0, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(stopPageLoad), window, NULL));
+
+ /* Load home page */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_Home, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(loadHomePage), window, NULL));
+
+ /* Zoom in, zoom out and default zoom*/
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_equal, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(zoomInCallback), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_KP_Add, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(zoomInCallback), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_minus, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(zoomOutCallback), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_KP_Subtract, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(zoomOutCallback), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_0, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(defaultZoomCallback), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_KP_0, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(defaultZoomCallback), window, NULL));
+
+ /* Toggle fullscreen */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_F11, 0, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(toggleFullScreen), window, NULL));
+
+ /* Quit */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_Q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(gtk_widget_destroy), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_W, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(gtk_widget_destroy), window, NULL));
+
+ GtkWidget *toolbar = gtk_toolbar_new();
+ window->toolbar = toolbar;
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_HORIZONTAL);
+ gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ);
+
+ GtkToolItem *item = gtk_menu_tool_button_new_from_stock(GTK_STOCK_GO_BACK);
+ window->backItem = GTK_WIDGET(item);
+ gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(item), 0);
+ g_signal_connect_swapped(item, "clicked", G_CALLBACK(goBackCallback), (gpointer)window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_menu_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD);
+ window->forwardItem = GTK_WIDGET(item);
+ gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(item), 0);
+ g_signal_connect_swapped(G_OBJECT(item), "clicked", G_CALLBACK(goForwardCallback), (gpointer)window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_PREFERENCES);
+ g_signal_connect_swapped(G_OBJECT(item), "clicked", G_CALLBACK(settingsCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_ZOOM_OUT);
+ window->zoomOutItem = GTK_WIDGET(item);
+ g_signal_connect_swapped(item, "clicked", G_CALLBACK(zoomOutCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_ZOOM_IN);
+ window->zoomInItem = GTK_WIDGET(item);
+ g_signal_connect_swapped(item, "clicked", G_CALLBACK(zoomInCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_FIND);
+ g_signal_connect_swapped(item, "clicked", G_CALLBACK(searchCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_add_accelerator(GTK_WIDGET(item), "clicked", window->accelGroup, GDK_KEY_F, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_HOME);
+ g_signal_connect_swapped(item, "clicked", G_CALLBACK(loadHomePage), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_add_accelerator(GTK_WIDGET(item), "clicked", window->accelGroup, GDK_KEY_Home, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_item_new();
+ gtk_tool_item_set_expand(item, TRUE);
+ gtk_container_add(GTK_CONTAINER(item), window->uriEntry);
+ gtk_widget_show(window->uriEntry);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH);
+ window->reloadOrStopButton = GTK_WIDGET(item);
+ g_signal_connect_swapped(item, "clicked", G_CALLBACK(reloadOrStopCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_add_accelerator(window->reloadOrStopButton, "clicked", window->accelGroup, GDK_KEY_F5, 0, GTK_ACCEL_VISIBLE);
+ gtk_widget_show(window->reloadOrStopButton);
+
+ GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ window->mainBox = vbox;
+ gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
+ gtk_widget_show(toolbar);
+
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+ gtk_widget_show(vbox);
+}
+
+static void browserWindowConstructed(GObject *gObject)
+{
+ BrowserWindow *window = BROWSER_WINDOW(gObject);
+
+ browserWindowUpdateZoomActions(window);
+ if (webkit_web_view_is_editable(window->webView)) {
+ browserWindowSetupEditorToolbar(window);
+ g_signal_connect(webkit_web_view_get_editor_state(window->webView), "notify::typing-attributes", G_CALLBACK(typingAttributesChanged), window);
+ }
+
+ g_signal_connect(window->webView, "notify::uri", G_CALLBACK(webViewURIChanged), window);
+ g_signal_connect(window->webView, "notify::estimated-load-progress", G_CALLBACK(webViewLoadProgressChanged), window);
+ g_signal_connect(window->webView, "notify::title", G_CALLBACK(webViewTitleChanged), window);
+ g_signal_connect(window->webView, "create", G_CALLBACK(webViewCreate), window);
+ g_signal_connect(window->webView, "load-failed", G_CALLBACK(webViewLoadFailed), window);
+ g_signal_connect(window->webView, "load-failed-with-tls-errors", G_CALLBACK(webViewLoadFailedWithTLSerrors), window);
+ g_signal_connect(window->webView, "decide-policy", G_CALLBACK(webViewDecidePolicy), window);
+ g_signal_connect(window->webView, "permission-request", G_CALLBACK(webViewDecidePermissionRequest), window);
+ g_signal_connect(window->webView, "mouse-target-changed", G_CALLBACK(webViewMouseTargetChanged), window);
+ g_signal_connect(window->webView, "notify::zoom-level", G_CALLBACK(webViewZoomLevelChanged), window);
+ g_signal_connect(window->webView, "notify::favicon", G_CALLBACK(faviconChanged), window);
+ g_signal_connect(window->webView, "enter-fullscreen", G_CALLBACK(webViewEnterFullScreen), window);
+ g_signal_connect(window->webView, "leave-fullscreen", G_CALLBACK(webViewLeaveFullScreen), window);
+ g_signal_connect(window->webView, "notify::is-loading", G_CALLBACK(webViewIsLoadingChanged), window);
+ g_signal_connect(window->webView, "scroll-event", G_CALLBACK(scrollEventCallback), window);
+#if GTK_CHECK_VERSION(3, 12, 0)
+ g_signal_connect(window->webView, "run-color-chooser", G_CALLBACK(runColorChooserCallback), window);
+#endif
+
+ g_signal_connect(webkit_web_view_get_context(window->webView), "download-started", G_CALLBACK(downloadStarted), window);
+
+ window->searchBar = BROWSER_SEARCH_BAR(browser_search_bar_new(window->webView));
+ browser_search_bar_add_accelerators(window->searchBar, window->accelGroup);
+ gtk_box_pack_start(GTK_BOX(window->mainBox), GTK_WIDGET(window->searchBar), FALSE, FALSE, 0);
+
+ WebKitBackForwardList *backForwadlist = webkit_web_view_get_back_forward_list(window->webView);
+ g_signal_connect(backForwadlist, "changed", G_CALLBACK(backForwadlistChanged), window);
+
+ WebKitWebInspector *inspectorWindow = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(window->webView));
+ g_signal_connect(inspectorWindow, "open-window", G_CALLBACK(inspectorWasOpenedInAnotherWindow), window);
+ g_signal_connect(inspectorWindow, "closed", G_CALLBACK(inspectorWasClosed), window);
+
+ GtkWidget *overlay = gtk_overlay_new();
+ gtk_box_pack_start(GTK_BOX(window->mainBox), overlay, TRUE, TRUE, 0);
+ gtk_widget_show(overlay);
+
+ window->statusLabel = gtk_label_new(NULL);
+ gtk_widget_set_halign(window->statusLabel, GTK_ALIGN_START);
+ gtk_widget_set_valign(window->statusLabel, GTK_ALIGN_END);
+ gtk_widget_set_margin_left(window->statusLabel, 1);
+ gtk_widget_set_margin_right(window->statusLabel, 1);
+ gtk_widget_set_margin_top(window->statusLabel, 1);
+ gtk_widget_set_margin_bottom(window->statusLabel, 1);
+ gtk_overlay_add_overlay(GTK_OVERLAY(overlay), window->statusLabel);
+
+ gtk_container_add(GTK_CONTAINER(overlay), GTK_WIDGET(window->webView));
+
+ window->fullScreenMessageLabel = gtk_label_new(NULL);
+ gtk_widget_set_halign(window->fullScreenMessageLabel, GTK_ALIGN_CENTER);
+ gtk_widget_set_valign(window->fullScreenMessageLabel, GTK_ALIGN_CENTER);
+ gtk_widget_set_no_show_all(window->fullScreenMessageLabel, TRUE);
+ gtk_overlay_add_overlay(GTK_OVERLAY(overlay), window->fullScreenMessageLabel);
+ gtk_widget_show(GTK_WIDGET(window->webView));
+
+ if (webkit_web_view_is_editable(window->webView))
+ webkit_web_view_load_html(window->webView, "<html></html>", "file:///");
+}
+
+static void browser_window_class_init(BrowserWindowClass *klass)
+{
+ GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
+
+ gobjectClass->constructed = browserWindowConstructed;
+ gobjectClass->get_property = browserWindowGetProperty;
+ gobjectClass->set_property = browserWindowSetProperty;
+ gobjectClass->finalize = browserWindowFinalize;
+
+ g_object_class_install_property(gobjectClass,
+ PROP_VIEW,
+ g_param_spec_object("view",
+ "View",
+ "The web view of this window",
+ WEBKIT_TYPE_WEB_VIEW,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+// Public API.
+GtkWidget *browser_window_new(WebKitWebView *view, GtkWindow *parent)
+{
+ g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(view), 0);
+
+ BrowserWindow *window = BROWSER_WINDOW(g_object_new(BROWSER_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL, "view", view, NULL));
+
+ if (parent) {
+ window->parentWindow = parent;
+ g_object_add_weak_pointer(G_OBJECT(parent), (gpointer *)&window->parentWindow);
+ }
+
+ return GTK_WIDGET(window);
+}
+
+WebKitWebView *browser_window_get_view(BrowserWindow *window)
+{
+ g_return_val_if_fail(BROWSER_IS_WINDOW(window), 0);
+
+ return window->webView;
+}
+
+void browser_window_load_uri(BrowserWindow *window, const char *uri)
+{
+ g_return_if_fail(BROWSER_IS_WINDOW(window));
+ g_return_if_fail(uri);
+
+ if (!g_str_has_prefix(uri, "javascript:")) {
+ char *internalURI = getInternalURI(uri);
+ webkit_web_view_load_uri(window->webView, internalURI);
+ g_free(internalURI);
+ return;
+ }
+
+ webkit_web_view_run_javascript(window->webView, strstr(uri, "javascript:"), NULL, NULL, NULL);
+}
+
+void browser_window_set_background_color(BrowserWindow *window, GdkRGBA *rgba)
+{
+ g_return_if_fail(BROWSER_IS_WINDOW(window));
+ g_return_if_fail(rgba);
+
+ GdkRGBA viewRGBA;
+ webkit_web_view_get_background_color(window->webView, &viewRGBA);
+ if (gdk_rgba_equal(rgba, &viewRGBA))
+ return;
+
+ if (rgba->alpha < 1) {
+ GdkVisual *rgbaVisual = gdk_screen_get_rgba_visual(gtk_window_get_screen(GTK_WINDOW(window)));
+ if (!rgbaVisual)
+ return;
+
+ gtk_widget_set_visual(GTK_WIDGET(window), rgbaVisual);
+ gtk_widget_set_app_paintable(GTK_WIDGET(window), TRUE);
+ }
+
+ webkit_web_view_set_background_color(window->webView, rgba);
+}
diff --git a/Tools/MiniBrowser/gtk/BrowserWindow.h b/Tools/MiniBrowser/gtk/BrowserWindow.h
new file mode 100644
index 000000000..d061e40f4
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserWindow.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BrowserWindow_h
+#define BrowserWindow_h
+
+#include <webkit2/webkit2.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define BROWSER_TYPE_WINDOW (browser_window_get_type())
+#define BROWSER_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BROWSER_TYPE_WINDOW, BrowserWindow))
+#define BROWSER_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), BROWSER_TYPE_WINDOW, BrowserWindowClass))
+#define BROWSER_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BROWSER_TYPE_WINDOW))
+#define BROWSER_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BROWSER_TYPE_WINDOW))
+#define BROWSER_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), BROWSER_TYPE_WINDOW, BrowserWindowClass))
+#define BROWSER_DEFAULT_URL "http://www.webkitgtk.org/"
+
+typedef struct _BrowserWindow BrowserWindow;
+typedef struct _BrowserWindowClass BrowserWindowClass;
+
+GType browser_window_get_type(void);
+
+GtkWidget* browser_window_new(WebKitWebView*, GtkWindow*);
+WebKitWebView* browser_window_get_view(BrowserWindow*);
+void browser_window_load_uri(BrowserWindow *, const char *uri);
+void browser_window_set_background_color(BrowserWindow*, GdkRGBA*);
+
+G_END_DECLS
+
+#endif
diff --git a/Tools/MiniBrowser/gtk/CMakeLists.txt b/Tools/MiniBrowser/gtk/CMakeLists.txt
new file mode 100644
index 000000000..94eecfa13
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/CMakeLists.txt
@@ -0,0 +1,63 @@
+set(MINIBROWSER_DIR "${TOOLS_DIR}/MiniBrowser/gtk")
+set(DERIVED_SOURCES_MINIBROWSER_DIR "${CMAKE_BINARY_DIR}/DerivedSources/MiniBrowser")
+
+file(MAKE_DIRECTORY ${DERIVED_SOURCES_MINIBROWSER_DIR})
+
+set(MiniBrowser_SOURCES
+ ${DERIVED_SOURCES_MINIBROWSER_DIR}/BrowserMarshal.c
+ ${MINIBROWSER_DIR}/BrowserCellRendererVariant.c
+ ${MINIBROWSER_DIR}/BrowserCellRendererVariant.h
+ ${MINIBROWSER_DIR}/BrowserDownloadsBar.c
+ ${MINIBROWSER_DIR}/BrowserDownloadsBar.h
+ ${MINIBROWSER_DIR}/BrowserSearchBar.c
+ ${MINIBROWSER_DIR}/BrowserSearchBar.h
+ ${MINIBROWSER_DIR}/BrowserSettingsDialog.c
+ ${MINIBROWSER_DIR}/BrowserSettingsDialog.h
+ ${MINIBROWSER_DIR}/BrowserWindow.c
+ ${MINIBROWSER_DIR}/BrowserWindow.h
+ ${MINIBROWSER_DIR}/main.c
+)
+
+set(MiniBrowser_INCLUDE_DIRECTORIES
+ ${CMAKE_BINARY_DIR}
+ ${DERIVED_SOURCES_MINIBROWSER_DIR}
+ ${DERIVED_SOURCES_WEBKIT2GTK_DIR}
+ ${FORWARDING_HEADERS_WEBKIT2GTK_DIR}
+ ${FORWARDING_HEADERS_DIR}
+ ${CMAKE_SOURCE_DIR}/Source
+)
+
+set(MiniBrowser_SYSTEM_INCLUDE_DIRECTORIES
+ ${GTK3_INCLUDE_DIRS}
+ ${GLIB_INCLUDE_DIRS}
+ ${LIBSOUP_INCLUDE_DIRS}
+)
+
+set(MiniBrowser_LIBRARIES
+ ${JavaScriptCore_LIBRARY_NAME}
+ WebKit2
+ ${GTK3_LIBRARIES}
+ ${GLIB_LIBRARIES}
+)
+
+add_custom_command(
+ OUTPUT ${DERIVED_SOURCES_MINIBROWSER_DIR}/BrowserMarshal.c
+ ${DERIVED_SOURCES_MINIBROWSER_DIR}/BrowserMarshal.h
+ MAIN_DEPENDENCY ${MINIBROWSER_DIR}/browser-marshal.list
+ COMMAND glib-genmarshal --prefix=browser_marshal ${MINIBROWSER_DIR}/browser-marshal.list --body > ${DERIVED_SOURCES_MINIBROWSER_DIR}/BrowserMarshal.c
+ COMMAND glib-genmarshal --prefix=browser_marshal ${MINIBROWSER_DIR}/browser-marshal.list --header > ${DERIVED_SOURCES_MINIBROWSER_DIR}/BrowserMarshal.h
+ VERBATIM)
+
+if (DEVELOPER_MODE)
+ add_definitions(-DWEBKIT_INJECTED_BUNDLE_PATH="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+endif ()
+
+add_definitions(-DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_6)
+
+include_directories(${MiniBrowser_INCLUDE_DIRECTORIES})
+include_directories(SYSTEM ${MiniBrowser_SYSTEM_INCLUDE_DIRECTORIES})
+add_executable(MiniBrowser ${MiniBrowser_SOURCES})
+target_link_libraries(MiniBrowser ${MiniBrowser_LIBRARIES})
+set_target_properties(MiniBrowser PROPERTIES FOLDER "Tools")
+
+install(TARGETS MiniBrowser DESTINATION "${EXEC_INSTALL_DIR}")
diff --git a/Tools/MiniBrowser/gtk/browser-marshal.list b/Tools/MiniBrowser/gtk/browser-marshal.list
new file mode 100644
index 000000000..e72aa4bc3
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/browser-marshal.list
@@ -0,0 +1 @@
+VOID:STRING,BOXED
diff --git a/Tools/MiniBrowser/gtk/main.c b/Tools/MiniBrowser/gtk/main.c
new file mode 100644
index 000000000..f5f6e176a
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/main.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cmakeconfig.h"
+
+#include "BrowserWindow.h"
+#include <errno.h>
+#include <gtk/gtk.h>
+#include <string.h>
+#include <webkit2/webkit2.h>
+
+#define MINI_BROWSER_ERROR (miniBrowserErrorQuark())
+
+static const gchar **uriArguments = NULL;
+static const char *miniBrowserAboutScheme = "minibrowser-about";
+static GdkRGBA *backgroundColor;
+static gboolean editorMode;
+
+typedef enum {
+ MINI_BROWSER_ERROR_INVALID_ABOUT_PATH
+} MiniBrowserError;
+
+static GQuark miniBrowserErrorQuark()
+{
+ return g_quark_from_string("minibrowser-quark");
+}
+
+static gchar *argumentToURL(const char *filename)
+{
+ GFile *gfile = g_file_new_for_commandline_arg(filename);
+ gchar *fileURL = g_file_get_uri(gfile);
+ g_object_unref(gfile);
+
+ return fileURL;
+}
+
+static void createBrowserWindow(const gchar *uri, WebKitSettings *webkitSettings)
+{
+ GtkWidget *webView = webkit_web_view_new();
+ if (editorMode)
+ webkit_web_view_set_editable(WEBKIT_WEB_VIEW(webView), TRUE);
+ GtkWidget *mainWindow = browser_window_new(WEBKIT_WEB_VIEW(webView), NULL);
+ if (backgroundColor)
+ browser_window_set_background_color(BROWSER_WINDOW(mainWindow), backgroundColor);
+
+ if (webkitSettings)
+ webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView), webkitSettings);
+
+ if (!editorMode) {
+ gchar *url = argumentToURL(uri);
+ browser_window_load_uri(BROWSER_WINDOW(mainWindow), url);
+ g_free(url);
+ }
+
+ gtk_widget_grab_focus(webView);
+ gtk_widget_show(mainWindow);
+}
+
+static gboolean parseBackgroundColor(const char *optionName, const char *value, gpointer data, GError **error)
+{
+ GdkRGBA rgba;
+ if (gdk_rgba_parse(&rgba, value)) {
+ backgroundColor = gdk_rgba_copy(&rgba);
+ return TRUE;
+ }
+
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Failed to parse '%s' as RGBA color", value);
+ return FALSE;
+}
+
+static const GOptionEntry commandLineOptions[] =
+{
+ { "bg-color", 0, 0, G_OPTION_ARG_CALLBACK, parseBackgroundColor, "Background color", NULL },
+ { "editor-mode", 'e', 0, G_OPTION_ARG_NONE, &editorMode, "Run in editor mode", NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, 0, "[URL…]" },
+ { 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static gboolean parseOptionEntryCallback(const gchar *optionNameFull, const gchar *value, WebKitSettings *webSettings, GError **error)
+{
+ if (strlen(optionNameFull) <= 2) {
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Invalid option %s", optionNameFull);
+ return FALSE;
+ }
+
+ /* We have two -- in option name so remove them. */
+ const gchar *optionName = optionNameFull + 2;
+ GParamSpec *spec = g_object_class_find_property(G_OBJECT_GET_CLASS(webSettings), optionName);
+ if (!spec) {
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Cannot find web settings for option %s", optionNameFull);
+ return FALSE;
+ }
+
+ switch (G_PARAM_SPEC_VALUE_TYPE(spec)) {
+ case G_TYPE_BOOLEAN: {
+ gboolean propertyValue = !(value && g_ascii_strcasecmp(value, "true") && strcmp(value, "1"));
+ g_object_set(G_OBJECT(webSettings), optionName, propertyValue, NULL);
+ break;
+ }
+ case G_TYPE_STRING:
+ g_object_set(G_OBJECT(webSettings), optionName, value, NULL);
+ break;
+ case G_TYPE_INT: {
+ glong propertyValue;
+ gchar *end;
+
+ errno = 0;
+ propertyValue = g_ascii_strtoll(value, &end, 0);
+ if (errno == ERANGE || propertyValue > G_MAXINT || propertyValue < G_MININT) {
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Integer value '%s' for %s out of range", value, optionNameFull);
+ return FALSE;
+ }
+ if (errno || value == end) {
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Cannot parse integer value '%s' for %s", value, optionNameFull);
+ return FALSE;
+ }
+ g_object_set(G_OBJECT(webSettings), optionName, propertyValue, NULL);
+ break;
+ }
+ case G_TYPE_FLOAT: {
+ gdouble propertyValue;
+ gchar *end;
+
+ errno = 0;
+ propertyValue = g_ascii_strtod(value, &end);
+ if (errno == ERANGE || propertyValue > G_MAXFLOAT || propertyValue < G_MINFLOAT) {
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Float value '%s' for %s out of range", value, optionNameFull);
+ return FALSE;
+ }
+ if (errno || value == end) {
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Cannot parse float value '%s' for %s", value, optionNameFull);
+ return FALSE;
+ }
+ g_object_set(G_OBJECT(webSettings), optionName, propertyValue, NULL);
+ break;
+ }
+ default:
+ g_assert_not_reached();
+ }
+
+ return TRUE;
+}
+
+static gboolean isValidParameterType(GType gParamType)
+{
+ return (gParamType == G_TYPE_BOOLEAN || gParamType == G_TYPE_STRING || gParamType == G_TYPE_INT
+ || gParamType == G_TYPE_FLOAT);
+}
+
+static GOptionEntry* getOptionEntriesFromWebKitSettings(WebKitSettings *webSettings)
+{
+ GParamSpec **propertySpecs;
+ GOptionEntry *optionEntries;
+ guint numProperties, numEntries, i;
+
+ propertySpecs = g_object_class_list_properties(G_OBJECT_GET_CLASS(webSettings), &numProperties);
+ if (!propertySpecs)
+ return NULL;
+
+ optionEntries = g_new0(GOptionEntry, numProperties + 1);
+ numEntries = 0;
+ for (i = 0; i < numProperties; i++) {
+ GParamSpec *param = propertySpecs[i];
+
+ /* Fill in structures only for writable and not construct-only properties. */
+ if (!param || !(param->flags & G_PARAM_WRITABLE) || (param->flags & G_PARAM_CONSTRUCT_ONLY))
+ continue;
+
+ GType gParamType = G_PARAM_SPEC_VALUE_TYPE(param);
+ if (!isValidParameterType(gParamType))
+ continue;
+
+ GOptionEntry *optionEntry = &optionEntries[numEntries++];
+ optionEntry->long_name = g_param_spec_get_name(param);
+
+ /* There is no easy way to figure our short name for generated option entries.
+ optionEntry.short_name=*/
+ /* For bool arguments "enable" type make option argument not required. */
+ if (gParamType == G_TYPE_BOOLEAN && (strstr(optionEntry->long_name, "enable")))
+ optionEntry->flags = G_OPTION_FLAG_OPTIONAL_ARG;
+ optionEntry->arg = G_OPTION_ARG_CALLBACK;
+ optionEntry->arg_data = parseOptionEntryCallback;
+ optionEntry->description = g_param_spec_get_blurb(param);
+ optionEntry->arg_description = g_type_name(gParamType);
+ }
+ g_free(propertySpecs);
+
+ return optionEntries;
+}
+
+static gboolean addSettingsGroupToContext(GOptionContext *context, WebKitSettings* webkitSettings)
+{
+ GOptionEntry *optionEntries = getOptionEntriesFromWebKitSettings(webkitSettings);
+ if (!optionEntries)
+ return FALSE;
+
+ GOptionGroup *webSettingsGroup = g_option_group_new("websettings",
+ "WebKitSettings writable properties for default WebKitWebView",
+ "WebKitSettings properties",
+ webkitSettings,
+ NULL);
+ g_option_group_add_entries(webSettingsGroup, optionEntries);
+ g_free(optionEntries);
+
+ /* Option context takes ownership of the group. */
+ g_option_context_add_group(context, webSettingsGroup);
+
+ return TRUE;
+}
+
+static void
+aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, gpointer userData)
+{
+ GInputStream *stream;
+ gsize streamLength;
+ const gchar *path;
+ gchar *contents;
+ GError *error;
+
+ path = webkit_uri_scheme_request_get_path(request);
+ if (!g_strcmp0(path, "minibrowser")) {
+ contents = g_strdup_printf("<html><body><h1>WebKitGTK+ MiniBrowser</h1><p>The WebKit2 test browser of the GTK+ port.</p><p>WebKit version: %d.%d.%d</p></body></html>",
+ webkit_get_major_version(),
+ webkit_get_minor_version(),
+ webkit_get_micro_version());
+ streamLength = strlen(contents);
+ stream = g_memory_input_stream_new_from_data(contents, streamLength, g_free);
+
+ webkit_uri_scheme_request_finish(request, stream, streamLength, "text/html");
+ g_object_unref(stream);
+ } else {
+ error = g_error_new(MINI_BROWSER_ERROR, MINI_BROWSER_ERROR_INVALID_ABOUT_PATH, "Invalid about:%s page.", path);
+ webkit_uri_scheme_request_finish_error(request, error);
+ g_error_free(error);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ gtk_init(&argc, &argv);
+#if ENABLE_DEVELOPER_MODE
+ g_setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, FALSE);
+#endif
+
+ const gchar *singleprocess = g_getenv("MINIBROWSER_SINGLEPROCESS");
+ webkit_web_context_set_process_model(webkit_web_context_get_default(), (singleprocess && *singleprocess) ?
+ WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS : WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
+
+ GOptionContext *context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, commandLineOptions, 0);
+ g_option_context_add_group(context, gtk_get_option_group(TRUE));
+
+ WebKitSettings *webkitSettings = webkit_settings_new();
+ webkit_settings_set_enable_developer_extras(webkitSettings, TRUE);
+ webkit_settings_set_enable_webgl(webkitSettings, TRUE);
+ if (!addSettingsGroupToContext(context, webkitSettings))
+ g_clear_object(&webkitSettings);
+
+ GError *error = 0;
+ if (!g_option_context_parse(context, &argc, &argv, &error)) {
+ g_printerr("Cannot parse arguments: %s\n", error->message);
+ g_error_free(error);
+ g_option_context_free(context);
+
+ return 1;
+ }
+ g_option_context_free (context);
+
+ // Enable the favicon database, by specifying the default directory.
+ webkit_web_context_set_favicon_database_directory(webkit_web_context_get_default(), NULL);
+
+ webkit_web_context_register_uri_scheme(webkit_web_context_get_default(), miniBrowserAboutScheme, aboutURISchemeRequestCallback, NULL, NULL);
+
+ if (uriArguments) {
+ int i;
+
+ for (i = 0; uriArguments[i]; i++)
+ createBrowserWindow(uriArguments[i], webkitSettings);
+ } else
+ createBrowserWindow(BROWSER_DEFAULT_URL, webkitSettings);
+
+ g_clear_object(&webkitSettings);
+
+ gtk_main();
+
+ return 0;
+}
diff --git a/Tools/Scripts/VCSUtils.pm b/Tools/Scripts/VCSUtils.pm
new file mode 100644
index 000000000..335897996
--- /dev/null
+++ b/Tools/Scripts/VCSUtils.pm
@@ -0,0 +1,2215 @@
+# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
+# Copyright (C) 2009, 2010 Chris Jerdonek (chris.jerdonek@gmail.com)
+# Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.
+# Copyright (C) 2012 Daniel Bates (dbates@intudata.com)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of Apple Inc. ("Apple") nor the names of
+# its contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Module to share code to work with various version control systems.
+package VCSUtils;
+
+use strict;
+use warnings;
+
+use Cwd qw(); # "qw()" prevents warnings about redefining getcwd() with "use POSIX;"
+use English; # for $POSTMATCH, etc.
+use File::Basename;
+use File::Spec;
+use POSIX;
+use Term::ANSIColor qw(colored);
+
+BEGIN {
+ use Exporter ();
+ our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
+ $VERSION = 1.00;
+ @ISA = qw(Exporter);
+ @EXPORT = qw(
+ &applyGitBinaryPatchDelta
+ &callSilently
+ &canonicalizePath
+ &changeLogEmailAddress
+ &changeLogName
+ &chdirReturningRelativePath
+ &decodeGitBinaryChunk
+ &decodeGitBinaryPatch
+ &determineSVNRoot
+ &determineVCSRoot
+ &escapeSubversionPath
+ &exitStatus
+ &fixChangeLogPatch
+ &gitBranch
+ &gitTreeDirectory
+ &gitdiff2svndiff
+ &isGit
+ &isGitSVN
+ &isGitBranchBuild
+ &isGitDirectory
+ &isGitSVNDirectory
+ &isSVN
+ &isSVNDirectory
+ &isSVNVersion16OrNewer
+ &makeFilePathRelative
+ &mergeChangeLogs
+ &normalizePath
+ &parseChunkRange
+ &parseFirstEOL
+ &parsePatch
+ &pathRelativeToSVNRepositoryRootForPath
+ &possiblyColored
+ &prepareParsedPatch
+ &removeEOL
+ &runCommand
+ &runPatchCommand
+ &scmMoveOrRenameFile
+ &scmToggleExecutableBit
+ &setChangeLogDateAndReviewer
+ &svnRevisionForDirectory
+ &svnStatus
+ &toWindowsLineEndings
+ &gitCommitForSVNRevision
+ &listOfChangedFilesBetweenRevisions
+ );
+ %EXPORT_TAGS = ( );
+ @EXPORT_OK = ();
+}
+
+our @EXPORT_OK;
+
+my $gitBranch;
+my $gitRoot;
+my $isGit;
+my $isGitSVN;
+my $isGitBranchBuild;
+my $isSVN;
+my $svnVersion;
+
+# Project time zone for Cupertino, CA, US
+my $changeLogTimeZone = "PST8PDT";
+
+my $gitDiffStartRegEx = qr#^diff --git [^\r\n]+#;
+my $gitDiffStartWithPrefixRegEx = qr#^diff --git \w/(.+) \w/([^\r\n]+)#; # We suppose that --src-prefix and --dst-prefix don't contain a non-word character (\W) and end with '/'.
+my $gitDiffStartWithoutPrefixNoSpaceRegEx = qr#^diff --git (\S+) (\S+)$#;
+my $svnDiffStartRegEx = qr#^Index: ([^\r\n]+)#;
+my $gitDiffStartWithoutPrefixSourceDirectoryPrefixRegExp = qr#^diff --git ([^/]+/)#;
+my $svnPropertiesStartRegEx = qr#^Property changes on: ([^\r\n]+)#; # $1 is normally the same as the index path.
+my $svnPropertyStartRegEx = qr#^(Modified|Name|Added|Deleted): ([^\r\n]+)#; # $2 is the name of the property.
+my $svnPropertyValueStartRegEx = qr#^\s*(\+|-|Merged|Reverse-merged)\s*([^\r\n]+)#; # $2 is the start of the property's value (which may span multiple lines).
+my $svnPropertyValueNoNewlineRegEx = qr#\ No newline at end of property#;
+
+# This method is for portability. Return the system-appropriate exit
+# status of a child process.
+#
+# Args: pass the child error status returned by the last pipe close,
+# for example "$?".
+sub exitStatus($)
+{
+ my ($returnvalue) = @_;
+ if ($^O eq "MSWin32") {
+ return $returnvalue >> 8;
+ }
+ if (!WIFEXITED($returnvalue)) {
+ return 254;
+ }
+ return WEXITSTATUS($returnvalue);
+}
+
+# Call a function while suppressing STDERR, and return the return values
+# as an array.
+sub callSilently($@) {
+ my ($func, @args) = @_;
+
+ # The following pattern was taken from here:
+ # http://www.sdsc.edu/~moreland/courses/IntroPerl/docs/manual/pod/perlfunc/open.html
+ #
+ # Also see this Perl documentation (search for "open OLDERR"):
+ # http://perldoc.perl.org/functions/open.html
+ open(OLDERR, ">&STDERR");
+ close(STDERR);
+ my @returnValue = &$func(@args);
+ open(STDERR, ">&OLDERR");
+ close(OLDERR);
+
+ return @returnValue;
+}
+
+sub toWindowsLineEndings
+{
+ my ($text) = @_;
+ $text =~ s/\n/\r\n/g;
+ return $text;
+}
+
+# Note, this method will not error if the file corresponding to the $source path does not exist.
+sub scmMoveOrRenameFile
+{
+ my ($source, $destination) = @_;
+ return if ! -e $source;
+ if (isSVN()) {
+ my $escapedDestination = escapeSubversionPath($destination);
+ my $escapedSource = escapeSubversionPath($source);
+ system("svn", "move", $escapedSource, $escapedDestination);
+ } elsif (isGit()) {
+ system("git", "mv", $source, $destination);
+ }
+}
+
+# Note, this method will not error if the file corresponding to the path does not exist.
+sub scmToggleExecutableBit
+{
+ my ($path, $executableBitDelta) = @_;
+ return if ! -e $path;
+ if ($executableBitDelta == 1) {
+ scmAddExecutableBit($path);
+ } elsif ($executableBitDelta == -1) {
+ scmRemoveExecutableBit($path);
+ }
+}
+
+sub scmAddExecutableBit($)
+{
+ my ($path) = @_;
+
+ if (isSVN()) {
+ my $escapedPath = escapeSubversionPath($path);
+ system("svn", "propset", "svn:executable", "on", $escapedPath) == 0 or die "Failed to run 'svn propset svn:executable on $escapedPath'.";
+ } elsif (isGit()) {
+ chmod(0755, $path);
+ }
+}
+
+sub scmRemoveExecutableBit($)
+{
+ my ($path) = @_;
+
+ if (isSVN()) {
+ my $escapedPath = escapeSubversionPath($path);
+ system("svn", "propdel", "svn:executable", $escapedPath) == 0 or die "Failed to run 'svn propdel svn:executable $escapedPath'.";
+ } elsif (isGit()) {
+ chmod(0664, $path);
+ }
+}
+
+sub isGitDirectory($)
+{
+ my ($dir) = @_;
+ return system("cd $dir && git rev-parse > " . File::Spec->devnull() . " 2>&1") == 0;
+}
+
+sub isGit()
+{
+ return $isGit if defined $isGit;
+
+ $isGit = isGitDirectory(".");
+ return $isGit;
+}
+
+sub isGitSVNDirectory($)
+{
+ my ($directory) = @_;
+
+ my $savedWorkingDirectory = Cwd::getcwd();
+ chdir($directory);
+
+ # There doesn't seem to be an officially documented way to determine
+ # if you're in a git-svn checkout. The best suggestions seen so far
+ # all use something like the following:
+ my $output = `git config --get svn-remote.svn.fetch 2>& 1`;
+ $isGitSVN = $output ne '';
+ chdir($savedWorkingDirectory);
+ return $isGitSVN;
+}
+
+sub isGitSVN()
+{
+ return $isGitSVN if defined $isGitSVN;
+
+ $isGitSVN = isGitSVNDirectory(".");
+ return $isGitSVN;
+}
+
+sub gitDirectory()
+{
+ chomp(my $result = `git rev-parse --git-dir`);
+ return $result;
+}
+
+sub gitTreeDirectory()
+{
+ chomp(my $result = `git rev-parse --show-toplevel`);
+ return $result;
+}
+
+sub gitBisectStartBranch()
+{
+ my $bisectStartFile = File::Spec->catfile(gitDirectory(), "BISECT_START");
+ if (!-f $bisectStartFile) {
+ return "";
+ }
+ open(BISECT_START, $bisectStartFile) or die "Failed to open $bisectStartFile: $!";
+ chomp(my $result = <BISECT_START>);
+ close(BISECT_START);
+ return $result;
+}
+
+sub gitBranch()
+{
+ unless (defined $gitBranch) {
+ chomp($gitBranch = `git symbolic-ref -q HEAD`);
+ my $hasDetachedHead = exitStatus($?);
+ if ($hasDetachedHead) {
+ # We may be in a git bisect session.
+ $gitBranch = gitBisectStartBranch();
+ }
+ $gitBranch =~ s#^refs/heads/##;
+ $gitBranch = "" if $gitBranch eq "master";
+ }
+
+ return $gitBranch;
+}
+
+sub isGitBranchBuild()
+{
+ my $branch = gitBranch();
+ chomp(my $override = `git config --bool branch.$branch.webKitBranchBuild`);
+ return 1 if $override eq "true";
+ return 0 if $override eq "false";
+
+ unless (defined $isGitBranchBuild) {
+ chomp(my $gitBranchBuild = `git config --bool core.webKitBranchBuild`);
+ $isGitBranchBuild = $gitBranchBuild eq "true";
+ }
+
+ return $isGitBranchBuild;
+}
+
+sub isSVNDirectory($)
+{
+ my ($dir) = @_;
+ return system("cd $dir && svn info > " . File::Spec->devnull() . " 2>&1") == 0;
+}
+
+sub isSVN()
+{
+ return $isSVN if defined $isSVN;
+
+ $isSVN = isSVNDirectory(".");
+ return $isSVN;
+}
+
+sub svnVersion()
+{
+ return $svnVersion if defined $svnVersion;
+
+ if (!isSVN()) {
+ $svnVersion = 0;
+ } else {
+ chomp($svnVersion = `svn --version --quiet`);
+ }
+ return $svnVersion;
+}
+
+sub isSVNVersion16OrNewer()
+{
+ my $version = svnVersion();
+ return eval "v$version" ge v1.6;
+}
+
+sub chdirReturningRelativePath($)
+{
+ my ($directory) = @_;
+ my $previousDirectory = Cwd::getcwd();
+ chdir $directory;
+ my $newDirectory = Cwd::getcwd();
+ return "." if $newDirectory eq $previousDirectory;
+ return File::Spec->abs2rel($previousDirectory, $newDirectory);
+}
+
+sub determineSVNRoot()
+{
+ my $last = '';
+ my $path = '.';
+ my $parent = '..';
+ my $repositoryRoot;
+ my $repositoryUUID;
+ while (1) {
+ my $thisRoot;
+ my $thisUUID;
+ my $escapedPath = escapeSubversionPath($path);
+ # Ignore error messages in case we've run past the root of the checkout.
+ open INFO, "svn info '$escapedPath' 2> " . File::Spec->devnull() . " |" or die;
+ while (<INFO>) {
+ if (/^Repository Root: (.+)/) {
+ $thisRoot = $1;
+ }
+ if (/^Repository UUID: (.+)/) {
+ $thisUUID = $1;
+ }
+ if ($thisRoot && $thisUUID) {
+ local $/ = undef;
+ <INFO>; # Consume the rest of the input.
+ }
+ }
+ close INFO;
+
+ # It's possible (e.g. for developers of some ports) to have a WebKit
+ # checkout in a subdirectory of another checkout. So abort if the
+ # repository root or the repository UUID suddenly changes.
+ last if !$thisUUID;
+ $repositoryUUID = $thisUUID if !$repositoryUUID;
+ last if $thisUUID ne $repositoryUUID;
+
+ last if !$thisRoot;
+ $repositoryRoot = $thisRoot if !$repositoryRoot;
+ last if $thisRoot ne $repositoryRoot;
+
+ $last = $path;
+ $path = File::Spec->catdir($parent, $path);
+ }
+
+ return File::Spec->rel2abs($last);
+}
+
+sub determineVCSRoot()
+{
+ if (isGit()) {
+ # This is the working tree root. If WebKit is a submodule,
+ # then the relevant metadata directory is somewhere else.
+ return gitTreeDirectory();
+ }
+
+ if (!isSVN()) {
+ # Some users have a workflow where svn-create-patch, svn-apply and
+ # svn-unapply are used outside of multiple svn working directores,
+ # so warn the user and assume Subversion is being used in this case.
+ warn "Unable to determine VCS root for '" . Cwd::getcwd() . "'; assuming Subversion";
+ $isSVN = 1;
+ }
+
+ return determineSVNRoot();
+}
+
+sub isWindows()
+{
+ return ($^O eq "MSWin32") || 0;
+}
+
+sub svnRevisionForDirectory($)
+{
+ my ($dir) = @_;
+ my $revision;
+
+ if (isSVNDirectory($dir)) {
+ my $escapedDir = escapeSubversionPath($dir);
+ my $command = "svn info $escapedDir | grep Revision:";
+ $command = "LC_ALL=C $command" if !isWindows();
+ my $svnInfo = `$command`;
+ ($revision) = ($svnInfo =~ m/Revision: (\d+).*/g);
+ } elsif (isGitDirectory($dir)) {
+ my $command = "git log --grep=\"git-svn-id: \" -n 1 | grep git-svn-id:";
+ $command = "LC_ALL=C $command" if !isWindows();
+ $command = "cd $dir && $command";
+ my $gitLog = `$command`;
+ ($revision) = ($gitLog =~ m/ +git-svn-id: .+@(\d+) /g);
+ }
+ if (!defined($revision)) {
+ $revision = "unknown";
+ warn "Unable to determine current SVN revision in $dir";
+ }
+ return $revision;
+}
+
+sub pathRelativeToSVNRepositoryRootForPath($)
+{
+ my ($file) = @_;
+ my $relativePath = File::Spec->abs2rel($file);
+
+ my $svnInfo;
+ if (isSVN()) {
+ my $escapedRelativePath = escapeSubversionPath($relativePath);
+ my $command = "svn info $escapedRelativePath";
+ $command = "LC_ALL=C $command" if !isWindows();
+ $svnInfo = `$command`;
+ } elsif (isGit()) {
+ my $command = "git svn info $relativePath";
+ $command = "LC_ALL=C $command" if !isWindows();
+ $svnInfo = `$command`;
+ }
+
+ $svnInfo =~ /.*^URL: (.*?)$/m;
+ my $svnURL = $1;
+
+ $svnInfo =~ /.*^Repository Root: (.*?)$/m;
+ my $repositoryRoot = $1;
+
+ $svnURL =~ s/$repositoryRoot\///;
+ return $svnURL;
+}
+
+sub makeFilePathRelative($)
+{
+ my ($path) = @_;
+ return $path unless isGit();
+
+ unless (defined $gitRoot) {
+ chomp($gitRoot = `git rev-parse --show-cdup`);
+ }
+ return $gitRoot . $path;
+}
+
+sub normalizePath($)
+{
+ my ($path) = @_;
+ $path =~ s/\\/\//g;
+ return $path;
+}
+
+sub possiblyColored($$)
+{
+ my ($colors, $string) = @_;
+
+ if (-t STDOUT) {
+ return colored([$colors], $string);
+ } else {
+ return $string;
+ }
+}
+
+sub adjustPathForRecentRenamings($)
+{
+ my ($fullPath) = @_;
+
+ $fullPath =~ s|WebCore/webaudio|WebCore/Modules/webaudio|g;
+ $fullPath =~ s|JavaScriptCore/wtf|WTF/wtf|g;
+ $fullPath =~ s|test_expectations.txt|TestExpectations|g;
+
+ return $fullPath;
+}
+
+sub canonicalizePath($)
+{
+ my ($file) = @_;
+
+ # Remove extra slashes and '.' directories in path
+ $file = File::Spec->canonpath($file);
+
+ # Remove '..' directories in path
+ my @dirs = ();
+ foreach my $dir (File::Spec->splitdir($file)) {
+ if ($dir eq '..' && $#dirs >= 0 && $dirs[$#dirs] ne '..') {
+ pop(@dirs);
+ } else {
+ push(@dirs, $dir);
+ }
+ }
+ return ($#dirs >= 0) ? File::Spec->catdir(@dirs) : ".";
+}
+
+sub removeEOL($)
+{
+ my ($line) = @_;
+ return "" unless $line;
+
+ $line =~ s/[\r\n]+$//g;
+ return $line;
+}
+
+sub parseFirstEOL($)
+{
+ my ($fileHandle) = @_;
+
+ # Make input record separator the new-line character to simplify regex matching below.
+ my $savedInputRecordSeparator = $INPUT_RECORD_SEPARATOR;
+ $INPUT_RECORD_SEPARATOR = "\n";
+ my $firstLine = <$fileHandle>;
+ $INPUT_RECORD_SEPARATOR = $savedInputRecordSeparator;
+
+ return unless defined($firstLine);
+
+ my $eol;
+ if ($firstLine =~ /\r\n/) {
+ $eol = "\r\n";
+ } elsif ($firstLine =~ /\r/) {
+ $eol = "\r";
+ } elsif ($firstLine =~ /\n/) {
+ $eol = "\n";
+ }
+ return $eol;
+}
+
+sub firstEOLInFile($)
+{
+ my ($file) = @_;
+ my $eol;
+ if (open(FILE, $file)) {
+ $eol = parseFirstEOL(*FILE);
+ close(FILE);
+ }
+ return $eol;
+}
+
+# Parses a chunk range line into its components.
+#
+# A chunk range line has the form: @@ -L_1,N_1 +L_2,N_2 @@, where the pairs (L_1, N_1),
+# (L_2, N_2) are ranges that represent the starting line number and line count in the
+# original file and new file, respectively.
+#
+# Note, some versions of GNU diff may omit the comma and trailing line count (e.g. N_1),
+# in which case the omitted line count defaults to 1. For example, GNU diff may output
+# @@ -1 +1 @@, which is equivalent to @@ -1,1 +1,1 @@.
+#
+# This subroutine returns undef if given an invalid or malformed chunk range.
+#
+# Args:
+# $line: the line to parse.
+# $chunkSentinel: the sentinel that surrounds the chunk range information (defaults to "@@").
+#
+# Returns $chunkRangeHashRef
+# $chunkRangeHashRef: a hash reference representing the parts of a chunk range, as follows--
+# startingLine: the starting line in the original file.
+# lineCount: the line count in the original file.
+# newStartingLine: the new starting line in the new file.
+# newLineCount: the new line count in the new file.
+sub parseChunkRange($;$)
+{
+ my ($line, $chunkSentinel) = @_;
+ $chunkSentinel = "@@" if !$chunkSentinel;
+ my $chunkRangeRegEx = qr#^\Q$chunkSentinel\E -(\d+)(,(\d+))? \+(\d+)(,(\d+))? \Q$chunkSentinel\E#;
+ if ($line !~ /$chunkRangeRegEx/) {
+ return;
+ }
+ my %chunkRange;
+ $chunkRange{startingLine} = $1;
+ $chunkRange{lineCount} = defined($2) ? $3 : 1;
+ $chunkRange{newStartingLine} = $4;
+ $chunkRange{newLineCount} = defined($5) ? $6 : 1;
+ return \%chunkRange;
+}
+
+sub svnStatus($)
+{
+ my ($fullPath) = @_;
+ my $escapedFullPath = escapeSubversionPath($fullPath);
+ my $svnStatus;
+ open SVN, "svn status --non-interactive --non-recursive '$escapedFullPath' |" or die;
+ if (-d $fullPath) {
+ # When running "svn stat" on a directory, we can't assume that only one
+ # status will be returned (since any files with a status below the
+ # directory will be returned), and we can't assume that the directory will
+ # be first (since any files with unknown status will be listed first).
+ my $normalizedFullPath = File::Spec->catdir(File::Spec->splitdir($fullPath));
+ while (<SVN>) {
+ # Input may use a different EOL sequence than $/, so avoid chomp.
+ $_ = removeEOL($_);
+ my $normalizedStatPath = File::Spec->catdir(File::Spec->splitdir(substr($_, 7)));
+ if ($normalizedFullPath eq $normalizedStatPath) {
+ $svnStatus = "$_\n";
+ last;
+ }
+ }
+ # Read the rest of the svn command output to avoid a broken pipe warning.
+ local $/ = undef;
+ <SVN>;
+ }
+ else {
+ # Files will have only one status returned.
+ $svnStatus = removeEOL(<SVN>) . "\n";
+ }
+ close SVN;
+ return $svnStatus;
+}
+
+# Return whether the given file mode is executable in the source control
+# sense. We make this determination based on whether the executable bit
+# is set for "others" rather than the stronger condition that it be set
+# for the user, group, and others. This is sufficient for distinguishing
+# the default behavior in Git and SVN.
+#
+# Args:
+# $fileMode: A number or string representing a file mode in octal notation.
+sub isExecutable($)
+{
+ my $fileMode = shift;
+
+ return $fileMode % 2;
+}
+
+# Parse the Git diff header start line.
+#
+# Args:
+# $line: "diff --git" line.
+#
+# Returns the path of the target file.
+sub parseGitDiffStartLine($)
+{
+ my $line = shift;
+ $_ = $line;
+ if (/$gitDiffStartWithPrefixRegEx/ || /$gitDiffStartWithoutPrefixNoSpaceRegEx/) {
+ return $2;
+ }
+ # Assume the diff was generated with --no-prefix (e.g. git diff --no-prefix).
+ if (!/$gitDiffStartWithoutPrefixSourceDirectoryPrefixRegExp/) {
+ # FIXME: Moving top directory file is not supported (e.g diff --git A.txt B.txt).
+ die("Could not find '/' in \"diff --git\" line: \"$line\"; only non-prefixed git diffs (i.e. not generated with --no-prefix) that move a top-level directory file are supported.");
+ }
+ my $pathPrefix = $1;
+ if (!/^diff --git \Q$pathPrefix\E.+ (\Q$pathPrefix\E.+)$/) {
+ # FIXME: Moving a file through sub directories of top directory is not supported (e.g diff --git A/B.txt C/B.txt).
+ die("Could not find '/' in \"diff --git\" line: \"$line\"; only non-prefixed git diffs (i.e. not generated with --no-prefix) that move a file between top-level directories are supported.");
+ }
+ return $1;
+}
+
+# Parse the next Git diff header from the given file handle, and advance
+# the handle so the last line read is the first line after the header.
+#
+# This subroutine dies if given leading junk.
+#
+# Args:
+# $fileHandle: advanced so the last line read from the handle is the first
+# line of the header to parse. This should be a line
+# beginning with "diff --git".
+# $line: the line last read from $fileHandle
+#
+# Returns ($headerHashRef, $lastReadLine):
+# $headerHashRef: a hash reference representing a diff header, as follows--
+# copiedFromPath: the path from which the file was copied or moved if
+# the diff is a copy or move.
+# executableBitDelta: the value 1 or -1 if the executable bit was added or
+# removed, respectively. New and deleted files have
+# this value only if the file is executable, in which
+# case the value is 1 and -1, respectively.
+# indexPath: the path of the target file.
+# isBinary: the value 1 if the diff is for a binary file.
+# isDeletion: the value 1 if the diff is a file deletion.
+# isCopyWithChanges: the value 1 if the file was copied or moved and
+# the target file was changed in some way after being
+# copied or moved (e.g. if its contents or executable
+# bit were changed).
+# isNew: the value 1 if the diff is for a new file.
+# shouldDeleteSource: the value 1 if the file was copied or moved and
+# the source file was deleted -- i.e. if the copy
+# was actually a move.
+# svnConvertedText: the header text with some lines converted to SVN
+# format. Git-specific lines are preserved.
+# $lastReadLine: the line last read from $fileHandle.
+sub parseGitDiffHeader($$)
+{
+ my ($fileHandle, $line) = @_;
+
+ $_ = $line;
+
+ my $indexPath;
+ if (/$gitDiffStartRegEx/) {
+ # Use $POSTMATCH to preserve the end-of-line character.
+ my $eol = $POSTMATCH;
+
+ # The first and second paths can differ in the case of copies
+ # and renames. We use the second file path because it is the
+ # destination path.
+ $indexPath = adjustPathForRecentRenamings(parseGitDiffStartLine($_));
+
+ $_ = "Index: $indexPath$eol"; # Convert to SVN format.
+ } else {
+ die("Could not parse leading \"diff --git\" line: \"$line\".");
+ }
+
+ my $copiedFromPath;
+ my $foundHeaderEnding;
+ my $isBinary;
+ my $isDeletion;
+ my $isNew;
+ my $newExecutableBit = 0;
+ my $oldExecutableBit = 0;
+ my $shouldDeleteSource = 0;
+ my $similarityIndex = 0;
+ my $svnConvertedText;
+ while (1) {
+ # Temporarily strip off any end-of-line characters to simplify
+ # regex matching below.
+ s/([\n\r]+)$//;
+ my $eol = $1;
+
+ if (/^(deleted file|old) mode (\d+)/) {
+ $oldExecutableBit = (isExecutable($2) ? 1 : 0);
+ $isDeletion = 1 if $1 eq "deleted file";
+ } elsif (/^new( file)? mode (\d+)/) {
+ $newExecutableBit = (isExecutable($2) ? 1 : 0);
+ $isNew = 1 if $1;
+ } elsif (/^similarity index (\d+)%/) {
+ $similarityIndex = $1;
+ } elsif (/^copy from ([^\t\r\n]+)/) {
+ $copiedFromPath = $1;
+ } elsif (/^rename from ([^\t\r\n]+)/) {
+ # FIXME: Record this as a move rather than as a copy-and-delete.
+ # This will simplify adding rename support to svn-unapply.
+ # Otherwise, the hash for a deletion would have to know
+ # everything about the file being deleted in order to
+ # support undoing itself. Recording as a move will also
+ # permit us to use "svn move" and "git move".
+ $copiedFromPath = $1;
+ $shouldDeleteSource = 1;
+ } elsif (/^--- \S+/) {
+ # Convert to SVN format.
+ # We emit the suffix "\t(revision 0)" to handle $indexPath which contains a space character.
+ # The patch(1) command thinks a file path is characters before a tab.
+ # This suffix make our diff more closely match the SVN diff format.
+ $_ = "--- $indexPath\t(revision 0)";
+ } elsif (/^\+\+\+ \S+/) {
+ # Convert to SVN format.
+ # We emit the suffix "\t(working copy)" to handle $indexPath which contains a space character.
+ # The patch(1) command thinks a file path is characters before a tab.
+ # This suffix make our diff more closely match the SVN diff format.
+ $_ = "+++ $indexPath\t(working copy)";
+ $foundHeaderEnding = 1;
+ } elsif (/^GIT binary patch$/ ) {
+ $isBinary = 1;
+ $foundHeaderEnding = 1;
+ # The "git diff" command includes a line of the form "Binary files
+ # <path1> and <path2> differ" if the --binary flag is not used.
+ } elsif (/^Binary files / ) {
+ die("Error: the Git diff contains a binary file without the binary data in ".
+ "line: \"$_\". Be sure to use the --binary flag when invoking \"git diff\" ".
+ "with diffs containing binary files.");
+ }
+
+ $svnConvertedText .= "$_$eol"; # Also restore end-of-line characters.
+
+ $_ = <$fileHandle>; # Not defined if end-of-file reached.
+
+ last if (!defined($_) || /$gitDiffStartRegEx/ || $foundHeaderEnding);
+ }
+
+ my $executableBitDelta = $newExecutableBit - $oldExecutableBit;
+
+ my %header;
+
+ $header{copiedFromPath} = $copiedFromPath if $copiedFromPath;
+ $header{executableBitDelta} = $executableBitDelta if $executableBitDelta;
+ $header{indexPath} = $indexPath;
+ $header{isBinary} = $isBinary if $isBinary;
+ $header{isCopyWithChanges} = 1 if ($copiedFromPath && ($similarityIndex != 100 || $executableBitDelta));
+ $header{isDeletion} = $isDeletion if $isDeletion;
+ $header{isNew} = $isNew if $isNew;
+ $header{shouldDeleteSource} = $shouldDeleteSource if $shouldDeleteSource;
+ $header{svnConvertedText} = $svnConvertedText;
+
+ return (\%header, $_);
+}
+
+# Parse the next SVN diff header from the given file handle, and advance
+# the handle so the last line read is the first line after the header.
+#
+# This subroutine dies if given leading junk or if it could not detect
+# the end of the header block.
+#
+# Args:
+# $fileHandle: advanced so the last line read from the handle is the first
+# line of the header to parse. This should be a line
+# beginning with "Index:".
+# $line: the line last read from $fileHandle
+#
+# Returns ($headerHashRef, $lastReadLine):
+# $headerHashRef: a hash reference representing a diff header, as follows--
+# copiedFromPath: the path from which the file was copied if the diff
+# is a copy.
+# indexPath: the path of the target file, which is the path found in
+# the "Index:" line.
+# isBinary: the value 1 if the diff is for a binary file.
+# isNew: the value 1 if the diff is for a new file.
+# sourceRevision: the revision number of the source, if it exists. This
+# is the same as the revision number the file was copied
+# from, in the case of a file copy.
+# svnConvertedText: the header text converted to a header with the paths
+# in some lines corrected.
+# $lastReadLine: the line last read from $fileHandle.
+sub parseSvnDiffHeader($$)
+{
+ my ($fileHandle, $line) = @_;
+
+ $_ = $line;
+
+ my $indexPath;
+ if (/$svnDiffStartRegEx/) {
+ $indexPath = adjustPathForRecentRenamings($1);
+ } else {
+ die("First line of SVN diff does not begin with \"Index \": \"$_\"");
+ }
+
+ my $copiedFromPath;
+ my $foundHeaderEnding;
+ my $isBinary;
+ my $isNew;
+ my $sourceRevision;
+ my $svnConvertedText;
+ while (1) {
+ # Temporarily strip off any end-of-line characters to simplify
+ # regex matching below.
+ s/([\n\r]+)$//;
+ my $eol = $1;
+
+ # Fix paths on "---" and "+++" lines to match the leading
+ # index line.
+ if (s/^--- [^\t\n\r]+/--- $indexPath/) {
+ # ---
+ if (/^--- .+\(revision (\d+)\)/) {
+ $sourceRevision = $1;
+ $isNew = 1 if !$sourceRevision; # if revision 0.
+ if (/\(from (\S+):(\d+)\)$/) {
+ # The "from" clause is created by svn-create-patch, in
+ # which case there is always also a "revision" clause.
+ $copiedFromPath = $1;
+ die("Revision number \"$2\" in \"from\" clause does not match " .
+ "source revision number \"$sourceRevision\".") if ($2 != $sourceRevision);
+ }
+ }
+ } elsif (s/^\+\+\+ [^\t\n\r]+/+++ $indexPath/ || $isBinary && /^$/) {
+ $foundHeaderEnding = 1;
+ } elsif (/^Cannot display: file marked as a binary type.$/) {
+ $isBinary = 1;
+ # SVN 1.7 has an unusual display format for a binary diff. It repeats the first
+ # two lines of the diff header. For example:
+ # Index: test_file.swf
+ # ===================================================================
+ # Cannot display: file marked as a binary type.
+ # svn:mime-type = application/octet-stream
+ # Index: test_file.swf
+ # ===================================================================
+ # --- test_file.swf
+ # +++ test_file.swf
+ #
+ # ...
+ # Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==
+ # Therefore, we continue reading the diff header until we either encounter a line
+ # that begins with "+++" (SVN 1.7 or greater) or an empty line (SVN version less
+ # than 1.7).
+ }
+
+ $svnConvertedText .= "$_$eol"; # Also restore end-of-line characters.
+
+ $_ = <$fileHandle>; # Not defined if end-of-file reached.
+
+ last if (!defined($_) || !$isBinary && /$svnDiffStartRegEx/ || $foundHeaderEnding);
+ }
+
+ if (!$foundHeaderEnding) {
+ die("Did not find end of header block corresponding to index path \"$indexPath\".");
+ }
+
+ my %header;
+
+ $header{copiedFromPath} = $copiedFromPath if $copiedFromPath;
+ $header{indexPath} = $indexPath;
+ $header{isBinary} = $isBinary if $isBinary;
+ $header{isNew} = $isNew if $isNew;
+ $header{sourceRevision} = $sourceRevision if $sourceRevision;
+ $header{svnConvertedText} = $svnConvertedText;
+
+ return (\%header, $_);
+}
+
+# Parse the next diff header from the given file handle, and advance
+# the handle so the last line read is the first line after the header.
+#
+# This subroutine dies if given leading junk or if it could not detect
+# the end of the header block.
+#
+# Args:
+# $fileHandle: advanced so the last line read from the handle is the first
+# line of the header to parse. For SVN-formatted diffs, this
+# is a line beginning with "Index:". For Git, this is a line
+# beginning with "diff --git".
+# $line: the line last read from $fileHandle
+#
+# Returns ($headerHashRef, $lastReadLine):
+# $headerHashRef: a hash reference representing a diff header
+# copiedFromPath: the path from which the file was copied if the diff
+# is a copy.
+# executableBitDelta: the value 1 or -1 if the executable bit was added or
+# removed, respectively. New and deleted files have
+# this value only if the file is executable, in which
+# case the value is 1 and -1, respectively.
+# indexPath: the path of the target file.
+# isBinary: the value 1 if the diff is for a binary file.
+# isGit: the value 1 if the diff is Git-formatted.
+# isSvn: the value 1 if the diff is SVN-formatted.
+# sourceRevision: the revision number of the source, if it exists. This
+# is the same as the revision number the file was copied
+# from, in the case of a file copy.
+# svnConvertedText: the header text with some lines converted to SVN
+# format. Git-specific lines are preserved.
+# $lastReadLine: the line last read from $fileHandle.
+sub parseDiffHeader($$)
+{
+ my ($fileHandle, $line) = @_;
+
+ my $header; # This is a hash ref.
+ my $isGit;
+ my $isSvn;
+ my $lastReadLine;
+
+ if ($line =~ $svnDiffStartRegEx) {
+ $isSvn = 1;
+ ($header, $lastReadLine) = parseSvnDiffHeader($fileHandle, $line);
+ } elsif ($line =~ $gitDiffStartRegEx) {
+ $isGit = 1;
+ ($header, $lastReadLine) = parseGitDiffHeader($fileHandle, $line);
+ } else {
+ die("First line of diff does not begin with \"Index:\" or \"diff --git\": \"$line\"");
+ }
+
+ $header->{isGit} = $isGit if $isGit;
+ $header->{isSvn} = $isSvn if $isSvn;
+
+ return ($header, $lastReadLine);
+}
+
+# FIXME: The %diffHash "object" should not have an svnConvertedText property.
+# Instead, the hash object should store its information in a
+# structured way as properties. This should be done in a way so
+# that, if necessary, the text of an SVN or Git patch can be
+# reconstructed from the information in those hash properties.
+#
+# A %diffHash is a hash representing a source control diff of a single
+# file operation (e.g. a file modification, copy, or delete).
+#
+# These hashes appear, for example, in the parseDiff(), parsePatch(),
+# and prepareParsedPatch() subroutines of this package.
+#
+# The corresponding values are--
+#
+# copiedFromPath: the path from which the file was copied if the diff
+# is a copy.
+# executableBitDelta: the value 1 or -1 if the executable bit was added or
+# removed from the target file, respectively.
+# indexPath: the path of the target file. For SVN-formatted diffs,
+# this is the same as the path in the "Index:" line.
+# isBinary: the value 1 if the diff is for a binary file.
+# isDeletion: the value 1 if the diff is known from the header to be a deletion.
+# isGit: the value 1 if the diff is Git-formatted.
+# isNew: the value 1 if the dif is known from the header to be a new file.
+# isSvn: the value 1 if the diff is SVN-formatted.
+# sourceRevision: the revision number of the source, if it exists. This
+# is the same as the revision number the file was copied
+# from, in the case of a file copy.
+# svnConvertedText: the diff with some lines converted to SVN format.
+# Git-specific lines are preserved.
+
+# Parse one diff from a patch file created by svn-create-patch, and
+# advance the file handle so the last line read is the first line
+# of the next header block.
+#
+# This subroutine preserves any leading junk encountered before the header.
+#
+# Composition of an SVN diff
+#
+# There are three parts to an SVN diff: the header, the property change, and
+# the binary contents, in that order. Either the header or the property change
+# may be ommitted, but not both. If there are binary changes, then you always
+# have all three.
+#
+# Args:
+# $fileHandle: a file handle advanced to the first line of the next
+# header block. Leading junk is okay.
+# $line: the line last read from $fileHandle.
+# $optionsHashRef: a hash reference representing optional options to use
+# when processing a diff.
+# shouldNotUseIndexPathEOL: whether to use the line endings in the diff instead
+# instead of the line endings in the target file; the
+# value of 1 if svnConvertedText should use the line
+# endings in the diff.
+#
+# Returns ($diffHashRefs, $lastReadLine):
+# $diffHashRefs: A reference to an array of references to %diffHash hashes.
+# See the %diffHash documentation above.
+# $lastReadLine: the line last read from $fileHandle
+sub parseDiff($$;$)
+{
+ # FIXME: Adjust this method so that it dies if the first line does not
+ # match the start of a diff. This will require a change to
+ # parsePatch() so that parsePatch() skips over leading junk.
+ my ($fileHandle, $line, $optionsHashRef) = @_;
+
+ my $headerStartRegEx = $svnDiffStartRegEx; # SVN-style header for the default
+
+ my $headerHashRef; # Last header found, as returned by parseDiffHeader().
+ my $svnPropertiesHashRef; # Last SVN properties diff found, as returned by parseSvnDiffProperties().
+ my $svnText;
+ my $indexPathEOL;
+ my $numTextChunks = 0;
+ while (defined($line)) {
+ if (!$headerHashRef && ($line =~ $gitDiffStartRegEx)) {
+ # Then assume all diffs in the patch are Git-formatted. This
+ # block was made to be enterable at most once since we assume
+ # all diffs in the patch are formatted the same (SVN or Git).
+ $headerStartRegEx = $gitDiffStartRegEx;
+ }
+
+ if ($line =~ $svnPropertiesStartRegEx) {
+ my $propertyPath = $1;
+ if ($svnPropertiesHashRef || $headerHashRef && ($propertyPath ne $headerHashRef->{indexPath})) {
+ # This is the start of the second diff in the while loop, which happens to
+ # be a property diff. If $svnPropertiesHasRef is defined, then this is the
+ # second consecutive property diff, otherwise it's the start of a property
+ # diff for a file that only has property changes.
+ last;
+ }
+ ($svnPropertiesHashRef, $line) = parseSvnDiffProperties($fileHandle, $line);
+ next;
+ }
+ if ($line !~ $headerStartRegEx) {
+ # Then we are in the body of the diff.
+ my $isChunkRange = defined(parseChunkRange($line));
+ $numTextChunks += 1 if $isChunkRange;
+ my $nextLine = <$fileHandle>;
+ my $willAddNewLineAtEndOfFile = defined($nextLine) && $nextLine =~ /^\\ No newline at end of file$/;
+ if ($willAddNewLineAtEndOfFile) {
+ # Diff(1) always emits a LF character preceeding the line "\ No newline at end of file".
+ # We must preserve both the added LF character and the line ending of this sentinel line
+ # or patch(1) will complain.
+ $svnText .= $line . $nextLine;
+ $line = <$fileHandle>;
+ next;
+ }
+ if ($indexPathEOL && !$isChunkRange) {
+ # The chunk range is part of the body of the diff, but its line endings should't be
+ # modified or patch(1) will complain. So, we only modify non-chunk range lines.
+ $line =~ s/\r\n|\r|\n/$indexPathEOL/g;
+ }
+ $svnText .= $line;
+ $line = $nextLine;
+ next;
+ } # Otherwise, we found a diff header.
+
+ if ($svnPropertiesHashRef || $headerHashRef) {
+ # Then either we just processed an SVN property change or this
+ # is the start of the second diff header of this while loop.
+ last;
+ }
+
+ ($headerHashRef, $line) = parseDiffHeader($fileHandle, $line);
+ if (!$optionsHashRef || !$optionsHashRef->{shouldNotUseIndexPathEOL}) {
+ # FIXME: We shouldn't query the file system (via firstEOLInFile()) to determine the
+ # line endings of the file indexPath. Instead, either the caller to parseDiff()
+ # should provide this information or parseDiff() should take a delegate that it
+ # can use to query for this information.
+ $indexPathEOL = firstEOLInFile($headerHashRef->{indexPath}) if !$headerHashRef->{isNew} && !$headerHashRef->{isBinary};
+ }
+
+ $svnText .= $headerHashRef->{svnConvertedText};
+ }
+
+ my @diffHashRefs;
+
+ if ($headerHashRef->{shouldDeleteSource}) {
+ my %deletionHash;
+ $deletionHash{indexPath} = $headerHashRef->{copiedFromPath};
+ $deletionHash{isDeletion} = 1;
+ push @diffHashRefs, \%deletionHash;
+ }
+ if ($headerHashRef->{copiedFromPath}) {
+ my %copyHash;
+ $copyHash{copiedFromPath} = $headerHashRef->{copiedFromPath};
+ $copyHash{indexPath} = $headerHashRef->{indexPath};
+ $copyHash{sourceRevision} = $headerHashRef->{sourceRevision} if $headerHashRef->{sourceRevision};
+ if ($headerHashRef->{isSvn}) {
+ $copyHash{executableBitDelta} = $svnPropertiesHashRef->{executableBitDelta} if $svnPropertiesHashRef->{executableBitDelta};
+ }
+ push @diffHashRefs, \%copyHash;
+ }
+
+ # Note, the order of evaluation for the following if conditional has been explicitly chosen so that
+ # it evaluates to false when there is no headerHashRef (e.g. a property change diff for a file that
+ # only has property changes).
+ if ($headerHashRef->{isCopyWithChanges} || (%$headerHashRef && !$headerHashRef->{copiedFromPath})) {
+ # Then add the usual file modification.
+ my %diffHash;
+ # FIXME: We should expand this code to support other properties. In the future,
+ # parseSvnDiffProperties may return a hash whose keys are the properties.
+ if ($headerHashRef->{isSvn}) {
+ # SVN records the change to the executable bit in a separate property change diff
+ # that follows the contents of the diff, except for binary diffs. For binary
+ # diffs, the property change diff follows the diff header.
+ $diffHash{executableBitDelta} = $svnPropertiesHashRef->{executableBitDelta} if $svnPropertiesHashRef->{executableBitDelta};
+ } elsif ($headerHashRef->{isGit}) {
+ # Git records the change to the executable bit in the header of a diff.
+ $diffHash{executableBitDelta} = $headerHashRef->{executableBitDelta} if $headerHashRef->{executableBitDelta};
+ }
+ $diffHash{indexPath} = $headerHashRef->{indexPath};
+ $diffHash{isBinary} = $headerHashRef->{isBinary} if $headerHashRef->{isBinary};
+ $diffHash{isDeletion} = $headerHashRef->{isDeletion} if $headerHashRef->{isDeletion};
+ $diffHash{isGit} = $headerHashRef->{isGit} if $headerHashRef->{isGit};
+ $diffHash{isNew} = $headerHashRef->{isNew} if $headerHashRef->{isNew};
+ $diffHash{isSvn} = $headerHashRef->{isSvn} if $headerHashRef->{isSvn};
+ if (!$headerHashRef->{copiedFromPath}) {
+ # If the file was copied, then we have already incorporated the
+ # sourceRevision information into the change.
+ $diffHash{sourceRevision} = $headerHashRef->{sourceRevision} if $headerHashRef->{sourceRevision};
+ }
+ # FIXME: Remove the need for svnConvertedText. See the %diffHash
+ # code comments above for more information.
+ #
+ # Note, we may not always have SVN converted text since we intend
+ # to deprecate it in the future. For example, a property change
+ # diff for a file that only has property changes will not return
+ # any SVN converted text.
+ $diffHash{svnConvertedText} = $svnText if $svnText;
+ $diffHash{numTextChunks} = $numTextChunks if $svnText && !$headerHashRef->{isBinary};
+ push @diffHashRefs, \%diffHash;
+ }
+
+ if (!%$headerHashRef && $svnPropertiesHashRef) {
+ # A property change diff for a file that only has property changes.
+ my %propertyChangeHash;
+ $propertyChangeHash{executableBitDelta} = $svnPropertiesHashRef->{executableBitDelta} if $svnPropertiesHashRef->{executableBitDelta};
+ $propertyChangeHash{indexPath} = $svnPropertiesHashRef->{propertyPath};
+ $propertyChangeHash{isSvn} = 1;
+ push @diffHashRefs, \%propertyChangeHash;
+ }
+
+ return (\@diffHashRefs, $line);
+}
+
+# Parse an SVN property change diff from the given file handle, and advance
+# the handle so the last line read is the first line after this diff.
+#
+# For the case of an SVN binary diff, the binary contents will follow the
+# the property changes.
+#
+# This subroutine dies if the first line does not begin with "Property changes on"
+# or if the separator line that follows this line is missing.
+#
+# Args:
+# $fileHandle: advanced so the last line read from the handle is the first
+# line of the footer to parse. This line begins with
+# "Property changes on".
+# $line: the line last read from $fileHandle.
+#
+# Returns ($propertyHashRef, $lastReadLine):
+# $propertyHashRef: a hash reference representing an SVN diff footer.
+# propertyPath: the path of the target file.
+# executableBitDelta: the value 1 or -1 if the executable bit was added or
+# removed from the target file, respectively.
+# $lastReadLine: the line last read from $fileHandle.
+sub parseSvnDiffProperties($$)
+{
+ my ($fileHandle, $line) = @_;
+
+ $_ = $line;
+
+ my %footer;
+ if (/$svnPropertiesStartRegEx/) {
+ $footer{propertyPath} = $1;
+ } else {
+ die("Failed to find start of SVN property change, \"Property changes on \": \"$_\"");
+ }
+
+ # We advance $fileHandle two lines so that the next line that
+ # we process is $svnPropertyStartRegEx in a well-formed footer.
+ # A well-formed footer has the form:
+ # Property changes on: FileA
+ # ___________________________________________________________________
+ # Added: svn:executable
+ # + *
+ $_ = <$fileHandle>; # Not defined if end-of-file reached.
+ my $separator = "_" x 67;
+ if (defined($_) && /^$separator[\r\n]+$/) {
+ $_ = <$fileHandle>;
+ } else {
+ die("Failed to find separator line: \"$_\".");
+ }
+
+ # FIXME: We should expand this to support other SVN properties
+ # (e.g. return a hash of property key-values that represents
+ # all properties).
+ #
+ # Notice, we keep processing until we hit end-of-file or some
+ # line that does not resemble $svnPropertyStartRegEx, such as
+ # the empty line that precedes the start of the binary contents
+ # of a patch, or the start of the next diff (e.g. "Index:").
+ my $propertyHashRef;
+ while (defined($_) && /$svnPropertyStartRegEx/) {
+ ($propertyHashRef, $_) = parseSvnProperty($fileHandle, $_);
+ if ($propertyHashRef->{name} eq "svn:executable") {
+ # Notice, for SVN properties, propertyChangeDelta is always non-zero
+ # because a property can only be added or removed.
+ $footer{executableBitDelta} = $propertyHashRef->{propertyChangeDelta};
+ }
+ }
+
+ return(\%footer, $_);
+}
+
+# Parse the next SVN property from the given file handle, and advance the handle so the last
+# line read is the first line after the property.
+#
+# This subroutine dies if the first line is not a valid start of an SVN property,
+# or the property is missing a value, or the property change type (e.g. "Added")
+# does not correspond to the property value type (e.g. "+").
+#
+# Args:
+# $fileHandle: advanced so the last line read from the handle is the first
+# line of the property to parse. This should be a line
+# that matches $svnPropertyStartRegEx.
+# $line: the line last read from $fileHandle.
+#
+# Returns ($propertyHashRef, $lastReadLine):
+# $propertyHashRef: a hash reference representing a SVN property.
+# name: the name of the property.
+# value: the last property value. For instance, suppose the property is "Modified".
+# Then it has both a '-' and '+' property value in that order. Therefore,
+# the value of this key is the value of the '+' property by ordering (since
+# it is the last value).
+# propertyChangeDelta: the value 1 or -1 if the property was added or
+# removed, respectively.
+# $lastReadLine: the line last read from $fileHandle.
+sub parseSvnProperty($$)
+{
+ my ($fileHandle, $line) = @_;
+
+ $_ = $line;
+
+ my $propertyName;
+ my $propertyChangeType;
+ if (/$svnPropertyStartRegEx/) {
+ $propertyChangeType = $1;
+ $propertyName = $2;
+ } else {
+ die("Failed to find SVN property: \"$_\".");
+ }
+
+ $_ = <$fileHandle>; # Not defined if end-of-file reached.
+
+ if (defined($_) && defined(parseChunkRange($_, "##"))) {
+ # FIXME: We should validate the chunk range line that is part of an SVN 1.7
+ # property diff. For now, we ignore this line.
+ $_ = <$fileHandle>;
+ }
+
+ # The "svn diff" command neither inserts newline characters between property values
+ # nor between successive properties.
+ #
+ # As of SVN 1.7, "svn diff" may insert "\ No newline at end of property" after a
+ # property value that doesn't end in a newline.
+ #
+ # FIXME: We do not support property values that contain tailing newline characters
+ # as it is difficult to disambiguate these trailing newlines from the empty
+ # line that precedes the contents of a binary patch.
+ my $propertyValue;
+ my $propertyValueType;
+ while (defined($_) && /$svnPropertyValueStartRegEx/) {
+ # Note, a '-' property may be followed by a '+' property in the case of a "Modified"
+ # or "Name" property. We only care about the ending value (i.e. the '+' property)
+ # in such circumstances. So, we take the property value for the property to be its
+ # last parsed property value.
+ #
+ # FIXME: We may want to consider strictly enforcing a '-', '+' property ordering or
+ # add error checking to prevent '+', '+', ..., '+' and other invalid combinations.
+ $propertyValueType = $1;
+ ($propertyValue, $_) = parseSvnPropertyValue($fileHandle, $_);
+ $_ = <$fileHandle> if defined($_) && /$svnPropertyValueNoNewlineRegEx/;
+ }
+
+ if (!$propertyValue) {
+ die("Failed to find the property value for the SVN property \"$propertyName\": \"$_\".");
+ }
+
+ my $propertyChangeDelta;
+ if ($propertyValueType eq "+" || $propertyValueType eq "Merged") {
+ $propertyChangeDelta = 1;
+ } elsif ($propertyValueType eq "-" || $propertyValueType eq "Reverse-merged") {
+ $propertyChangeDelta = -1;
+ } else {
+ die("Not reached.");
+ }
+
+ # We perform a simple validation that an "Added" or "Deleted" property
+ # change type corresponds with a "+" and "-" value type, respectively.
+ my $expectedChangeDelta;
+ if ($propertyChangeType eq "Added") {
+ $expectedChangeDelta = 1;
+ } elsif ($propertyChangeType eq "Deleted") {
+ $expectedChangeDelta = -1;
+ }
+
+ if ($expectedChangeDelta && $propertyChangeDelta != $expectedChangeDelta) {
+ die("The final property value type found \"$propertyValueType\" does not " .
+ "correspond to the property change type found \"$propertyChangeType\".");
+ }
+
+ my %propertyHash;
+ $propertyHash{name} = $propertyName;
+ $propertyHash{propertyChangeDelta} = $propertyChangeDelta;
+ $propertyHash{value} = $propertyValue;
+ return (\%propertyHash, $_);
+}
+
+# Parse the value of an SVN property from the given file handle, and advance
+# the handle so the last line read is the first line after the property value.
+#
+# This subroutine dies if the first line is an invalid SVN property value line
+# (i.e. a line that does not begin with " +" or " -").
+#
+# Args:
+# $fileHandle: advanced so the last line read from the handle is the first
+# line of the property value to parse. This should be a line
+# beginning with " +" or " -".
+# $line: the line last read from $fileHandle.
+#
+# Returns ($propertyValue, $lastReadLine):
+# $propertyValue: the value of the property.
+# $lastReadLine: the line last read from $fileHandle.
+sub parseSvnPropertyValue($$)
+{
+ my ($fileHandle, $line) = @_;
+
+ $_ = $line;
+
+ my $propertyValue;
+ my $eol;
+ if (/$svnPropertyValueStartRegEx/) {
+ $propertyValue = $2; # Does not include the end-of-line character(s).
+ $eol = $POSTMATCH;
+ } else {
+ die("Failed to find property value beginning with '+', '-', 'Merged', or 'Reverse-merged': \"$_\".");
+ }
+
+ while (<$fileHandle>) {
+ if (/^[\r\n]+$/ || /$svnPropertyValueStartRegEx/ || /$svnPropertyStartRegEx/ || /$svnPropertyValueNoNewlineRegEx/) {
+ # Note, we may encounter an empty line before the contents of a binary patch.
+ # Also, we check for $svnPropertyValueStartRegEx because a '-' property may be
+ # followed by a '+' property in the case of a "Modified" or "Name" property.
+ # We check for $svnPropertyStartRegEx because it indicates the start of the
+ # next property to parse.
+ last;
+ }
+
+ # Temporarily strip off any end-of-line characters. We add the end-of-line characters
+ # from the previously processed line to the start of this line so that the last line
+ # of the property value does not end in end-of-line characters.
+ s/([\n\r]+)$//;
+ $propertyValue .= "$eol$_";
+ $eol = $1;
+ }
+
+ return ($propertyValue, $_);
+}
+
+# Parse a patch file created by svn-create-patch.
+#
+# Args:
+# $fileHandle: A file handle to the patch file that has not yet been
+# read from.
+# $optionsHashRef: a hash reference representing optional options to use
+# when processing a diff.
+# shouldNotUseIndexPathEOL: whether to use the line endings in the diff instead
+# instead of the line endings in the target file; the
+# value of 1 if svnConvertedText should use the line
+# endings in the diff.
+#
+# Returns:
+# @diffHashRefs: an array of diff hash references.
+# See the %diffHash documentation above.
+sub parsePatch($;$)
+{
+ my ($fileHandle, $optionsHashRef) = @_;
+
+ my $newDiffHashRefs;
+ my @diffHashRefs; # return value
+
+ my $line = <$fileHandle>;
+
+ while (defined($line)) { # Otherwise, at EOF.
+
+ ($newDiffHashRefs, $line) = parseDiff($fileHandle, $line, $optionsHashRef);
+
+ push @diffHashRefs, @$newDiffHashRefs;
+ }
+
+ return @diffHashRefs;
+}
+
+# Prepare the results of parsePatch() for use in svn-apply and svn-unapply.
+#
+# Args:
+# $shouldForce: Whether to continue processing if an unexpected
+# state occurs.
+# @diffHashRefs: An array of references to %diffHashes.
+# See the %diffHash documentation above.
+#
+# Returns $preparedPatchHashRef:
+# copyDiffHashRefs: A reference to an array of the $diffHashRefs in
+# @diffHashRefs that represent file copies. The original
+# ordering is preserved.
+# nonCopyDiffHashRefs: A reference to an array of the $diffHashRefs in
+# @diffHashRefs that do not represent file copies.
+# The original ordering is preserved.
+# sourceRevisionHash: A reference to a hash of source path to source
+# revision number.
+sub prepareParsedPatch($@)
+{
+ my ($shouldForce, @diffHashRefs) = @_;
+
+ my %copiedFiles;
+
+ # Return values
+ my @copyDiffHashRefs = ();
+ my @nonCopyDiffHashRefs = ();
+ my %sourceRevisionHash = ();
+ for my $diffHashRef (@diffHashRefs) {
+ my $copiedFromPath = $diffHashRef->{copiedFromPath};
+ my $indexPath = $diffHashRef->{indexPath};
+ my $sourceRevision = $diffHashRef->{sourceRevision};
+ my $sourcePath;
+
+ if (defined($copiedFromPath)) {
+ # Then the diff is a copy operation.
+ $sourcePath = $copiedFromPath;
+
+ # FIXME: Consider printing a warning or exiting if
+ # exists($copiedFiles{$indexPath}) is true -- i.e. if
+ # $indexPath appears twice as a copy target.
+ $copiedFiles{$indexPath} = $sourcePath;
+
+ push @copyDiffHashRefs, $diffHashRef;
+ } else {
+ # Then the diff is not a copy operation.
+ $sourcePath = $indexPath;
+
+ push @nonCopyDiffHashRefs, $diffHashRef;
+ }
+
+ if (defined($sourceRevision)) {
+ if (exists($sourceRevisionHash{$sourcePath}) &&
+ ($sourceRevisionHash{$sourcePath} != $sourceRevision)) {
+ if (!$shouldForce) {
+ die "Two revisions of the same file required as a source:\n".
+ " $sourcePath:$sourceRevisionHash{$sourcePath}\n".
+ " $sourcePath:$sourceRevision";
+ }
+ }
+ $sourceRevisionHash{$sourcePath} = $sourceRevision;
+ }
+ }
+
+ my %preparedPatchHash;
+
+ $preparedPatchHash{copyDiffHashRefs} = \@copyDiffHashRefs;
+ $preparedPatchHash{nonCopyDiffHashRefs} = \@nonCopyDiffHashRefs;
+ $preparedPatchHash{sourceRevisionHash} = \%sourceRevisionHash;
+
+ return \%preparedPatchHash;
+}
+
+# Return localtime() for the project's time zone, given an integer time as
+# returned by Perl's time() function.
+sub localTimeInProjectTimeZone($)
+{
+ my $epochTime = shift;
+
+ # Change the time zone temporarily for the localtime() call.
+ my $savedTimeZone = $ENV{'TZ'};
+ $ENV{'TZ'} = $changeLogTimeZone;
+ my @localTime = localtime($epochTime);
+ if (defined $savedTimeZone) {
+ $ENV{'TZ'} = $savedTimeZone;
+ } else {
+ delete $ENV{'TZ'};
+ }
+
+ return @localTime;
+}
+
+# Set the reviewer and date in a ChangeLog patch, and return the new patch.
+#
+# Args:
+# $patch: a ChangeLog patch as a string.
+# $reviewer: the name of the reviewer, or undef if the reviewer should not be set.
+# $epochTime: an integer time as returned by Perl's time() function.
+sub setChangeLogDateAndReviewer($$$)
+{
+ my ($patch, $reviewer, $epochTime) = @_;
+
+ my @localTime = localTimeInProjectTimeZone($epochTime);
+ my $newDate = strftime("%Y-%m-%d", @localTime);
+
+ my $firstChangeLogLineRegEx = qr#(\n\+)\d{4}-[^-]{2}-[^-]{2}( )#;
+ $patch =~ s/$firstChangeLogLineRegEx/$1$newDate$2/;
+
+ if (defined($reviewer)) {
+ # We include a leading plus ("+") in the regular expression to make
+ # the regular expression less likely to match text in the leading junk
+ # for the patch, if the patch has leading junk.
+ $patch =~ s/(\n\+.*)NOBODY \(OOPS!\)/$1$reviewer/;
+ }
+
+ return $patch;
+}
+
+# If possible, returns a ChangeLog patch equivalent to the given one,
+# but with the newest ChangeLog entry inserted at the top of the
+# file -- i.e. no leading context and all lines starting with "+".
+#
+# If given a patch string not representable as a patch with the above
+# properties, it returns the input back unchanged.
+#
+# WARNING: This subroutine can return an inequivalent patch string if
+# both the beginning of the new ChangeLog file matches the beginning
+# of the source ChangeLog, and the source beginning was modified.
+# Otherwise, it is guaranteed to return an equivalent patch string,
+# if it returns.
+#
+# Applying this subroutine to ChangeLog patches allows svn-apply to
+# insert new ChangeLog entries at the top of the ChangeLog file.
+# svn-apply uses patch with --fuzz=3 to do this. We need to apply
+# this subroutine because the diff(1) command is greedy when matching
+# lines. A new ChangeLog entry with the same date and author as the
+# previous will match and cause the diff to have lines of starting
+# context.
+#
+# This subroutine has unit tests in VCSUtils_unittest.pl.
+#
+# Returns $changeLogHashRef:
+# $changeLogHashRef: a hash reference representing a change log patch.
+# patch: a ChangeLog patch equivalent to the given one, but with the
+# newest ChangeLog entry inserted at the top of the file, if possible.
+sub fixChangeLogPatch($)
+{
+ my $patch = shift; # $patch will only contain patch fragments for ChangeLog.
+
+ $patch =~ s|test_expectations.txt:|TestExpectations:|g;
+
+ $patch =~ /(\r?\n)/;
+ my $lineEnding = $1;
+ my @lines = split(/$lineEnding/, $patch);
+
+ my $i = 0; # We reuse the same index throughout.
+
+ # Skip to beginning of first chunk.
+ for (; $i < @lines; ++$i) {
+ if (substr($lines[$i], 0, 1) eq "@") {
+ last;
+ }
+ }
+ my $chunkStartIndex = ++$i;
+ my %changeLogHashRef;
+
+ # Optimization: do not process if new lines already begin the chunk.
+ if (substr($lines[$i], 0, 1) eq "+") {
+ $changeLogHashRef{patch} = $patch;
+ return \%changeLogHashRef;
+ }
+
+ # Skip to first line of newly added ChangeLog entry.
+ # For example, +2009-06-03 Eric Seidel <eric@webkit.org>
+ my $dateStartRegEx = '^\+(\d{4}-\d{2}-\d{2})' # leading "+" and date
+ . '\s+(.+)\s+' # name
+ . '<([^<>]+)>$'; # e-mail address
+
+ for (; $i < @lines; ++$i) {
+ my $line = $lines[$i];
+ my $firstChar = substr($line, 0, 1);
+ if ($line =~ /$dateStartRegEx/) {
+ last;
+ } elsif ($firstChar eq " " or $firstChar eq "+") {
+ next;
+ }
+ $changeLogHashRef{patch} = $patch; # Do not change if, for example, "-" or "@" found.
+ return \%changeLogHashRef;
+ }
+ if ($i >= @lines) {
+ $changeLogHashRef{patch} = $patch; # Do not change if date not found.
+ return \%changeLogHashRef;
+ }
+ my $dateStartIndex = $i;
+
+ # Rewrite overlapping lines to lead with " ".
+ my @overlappingLines = (); # These will include a leading "+".
+ for (; $i < @lines; ++$i) {
+ my $line = $lines[$i];
+ if (substr($line, 0, 1) ne "+") {
+ last;
+ }
+ push(@overlappingLines, $line);
+ $lines[$i] = " " . substr($line, 1);
+ }
+
+ # Remove excess ending context, if necessary.
+ my $shouldTrimContext = 1;
+ for (; $i < @lines; ++$i) {
+ my $firstChar = substr($lines[$i], 0, 1);
+ if ($firstChar eq " ") {
+ next;
+ } elsif ($firstChar eq "@") {
+ last;
+ }
+ $shouldTrimContext = 0; # For example, if "+" or "-" encountered.
+ last;
+ }
+ my $deletedLineCount = 0;
+ if ($shouldTrimContext) { # Also occurs if end of file reached.
+ splice(@lines, $i - @overlappingLines, @overlappingLines);
+ $deletedLineCount = @overlappingLines;
+ }
+
+ # Work backwards, shifting overlapping lines towards front
+ # while checking that patch stays equivalent.
+ for ($i = $dateStartIndex - 1; @overlappingLines && $i >= $chunkStartIndex; --$i) {
+ my $line = $lines[$i];
+ if (substr($line, 0, 1) ne " ") {
+ next;
+ }
+ my $text = substr($line, 1);
+ my $newLine = pop(@overlappingLines);
+ if ($text ne substr($newLine, 1)) {
+ $changeLogHashRef{patch} = $patch; # Unexpected difference.
+ return \%changeLogHashRef;
+ }
+ $lines[$i] = "+$text";
+ }
+
+ # If @overlappingLines > 0, this is where we make use of the
+ # assumption that the beginning of the source file was not modified.
+ splice(@lines, $chunkStartIndex, 0, @overlappingLines);
+
+ # Update the date start index as it may have changed after shifting
+ # the overlapping lines towards the front.
+ for ($i = $chunkStartIndex; $i < $dateStartIndex; ++$i) {
+ $dateStartIndex = $i if $lines[$i] =~ /$dateStartRegEx/;
+ }
+ splice(@lines, $chunkStartIndex, $dateStartIndex - $chunkStartIndex); # Remove context of later entry.
+ $deletedLineCount += $dateStartIndex - $chunkStartIndex;
+
+ # Update the initial chunk range.
+ my $chunkRangeHashRef = parseChunkRange($lines[$chunkStartIndex - 1]);
+ if (!$chunkRangeHashRef) {
+ # FIXME: Handle errors differently from ChangeLog files that
+ # are okay but should not be altered. That way we can find out
+ # if improvements to the script ever become necessary.
+ $changeLogHashRef{patch} = $patch; # Error: unexpected patch string format.
+ return \%changeLogHashRef;
+ }
+ my $oldSourceLineCount = $chunkRangeHashRef->{lineCount};
+ my $oldTargetLineCount = $chunkRangeHashRef->{newLineCount};
+
+ my $sourceLineCount = $oldSourceLineCount + @overlappingLines - $deletedLineCount;
+ my $targetLineCount = $oldTargetLineCount + @overlappingLines - $deletedLineCount;
+ $lines[$chunkStartIndex - 1] = "@@ -1,$sourceLineCount +1,$targetLineCount @@";
+
+ $changeLogHashRef{patch} = join($lineEnding, @lines) . "\n"; # patch(1) expects an extra trailing newline.
+ return \%changeLogHashRef;
+}
+
+# This is a supporting method for runPatchCommand.
+#
+# Arg: the optional $args parameter passed to runPatchCommand (can be undefined).
+#
+# Returns ($patchCommand, $isForcing).
+#
+# This subroutine has unit tests in VCSUtils_unittest.pl.
+sub generatePatchCommand($)
+{
+ my ($passedArgsHashRef) = @_;
+
+ my $argsHashRef = { # Defaults
+ ensureForce => 0,
+ shouldReverse => 0,
+ options => []
+ };
+
+ # Merges hash references. It's okay here if passed hash reference is undefined.
+ @{$argsHashRef}{keys %{$passedArgsHashRef}} = values %{$passedArgsHashRef};
+
+ my $ensureForce = $argsHashRef->{ensureForce};
+ my $shouldReverse = $argsHashRef->{shouldReverse};
+ my $options = $argsHashRef->{options};
+
+ if (! $options) {
+ $options = [];
+ } else {
+ $options = [@{$options}]; # Copy to avoid side effects.
+ }
+
+ my $isForcing = 0;
+ if (grep /^--force$/, @{$options}) {
+ $isForcing = 1;
+ } elsif ($ensureForce) {
+ push @{$options}, "--force";
+ $isForcing = 1;
+ }
+
+ if ($shouldReverse) { # No check: --reverse should never be passed explicitly.
+ push @{$options}, "--reverse";
+ }
+
+ @{$options} = sort(@{$options}); # For easier testing.
+
+ my $patchCommand = join(" ", "patch -p0", @{$options});
+
+ return ($patchCommand, $isForcing);
+}
+
+# Apply the given patch using the patch(1) command.
+#
+# On success, return the resulting exit status. Otherwise, exit with the
+# exit status. If "--force" is passed as an option, however, then never
+# exit and always return the exit status.
+#
+# Args:
+# $patch: a patch string.
+# $repositoryRootPath: an absolute path to the repository root.
+# $pathRelativeToRoot: the path of the file to be patched, relative to the
+# repository root. This should normally be the path
+# found in the patch's "Index:" line. It is passed
+# explicitly rather than reparsed from the patch
+# string for optimization purposes.
+# This is used only for error reporting. The
+# patch command gleans the actual file to patch
+# from the patch string.
+# $args: a reference to a hash of optional arguments. The possible
+# keys are --
+# ensureForce: whether to ensure --force is passed (defaults to 0).
+# shouldReverse: whether to pass --reverse (defaults to 0).
+# options: a reference to an array of options to pass to the
+# patch command. The subroutine passes the -p0 option
+# no matter what. This should not include --reverse.
+#
+# This subroutine has unit tests in VCSUtils_unittest.pl.
+sub runPatchCommand($$$;$)
+{
+ my ($patch, $repositoryRootPath, $pathRelativeToRoot, $args) = @_;
+
+ my ($patchCommand, $isForcing) = generatePatchCommand($args);
+
+ # Temporarily change the working directory since the path found
+ # in the patch's "Index:" line is relative to the repository root
+ # (i.e. the same as $pathRelativeToRoot).
+ my $cwd = Cwd::getcwd();
+ chdir $repositoryRootPath;
+
+ open PATCH, "| $patchCommand" or die "Could not call \"$patchCommand\" for file \"$pathRelativeToRoot\": $!";
+ print PATCH $patch;
+ close PATCH;
+ my $exitStatus = exitStatus($?);
+
+ chdir $cwd;
+
+ if ($exitStatus && !$isForcing) {
+ print "Calling \"$patchCommand\" for file \"$pathRelativeToRoot\" returned " .
+ "status $exitStatus. Pass --force to ignore patch failures.\n";
+ exit $exitStatus;
+ }
+
+ return $exitStatus;
+}
+
+# Merge ChangeLog patches using a three-file approach.
+#
+# This is used by resolve-ChangeLogs when it's operated as a merge driver
+# and when it's used to merge conflicts after a patch is applied or after
+# an svn update.
+#
+# It's also used for traditional rejected patches.
+#
+# Args:
+# $fileMine: The merged version of the file. Also known in git as the
+# other branch's version (%B) or "ours".
+# For traditional patch rejects, this is the *.rej file.
+# $fileOlder: The base version of the file. Also known in git as the
+# ancestor version (%O) or "base".
+# For traditional patch rejects, this is the *.orig file.
+# $fileNewer: The current version of the file. Also known in git as the
+# current version (%A) or "theirs".
+# For traditional patch rejects, this is the original-named
+# file.
+#
+# Returns 1 if merge was successful, else 0.
+sub mergeChangeLogs($$$)
+{
+ my ($fileMine, $fileOlder, $fileNewer) = @_;
+
+ my $traditionalReject = $fileMine =~ /\.rej$/ ? 1 : 0;
+
+ local $/ = undef;
+
+ my $patch;
+ if ($traditionalReject) {
+ open(DIFF, "<", $fileMine) or die $!;
+ $patch = <DIFF>;
+ close(DIFF);
+ rename($fileMine, "$fileMine.save");
+ rename($fileOlder, "$fileOlder.save");
+ } else {
+ open(DIFF, "diff -u -a --binary \"$fileOlder\" \"$fileMine\" |") or die $!;
+ $patch = <DIFF>;
+ close(DIFF);
+ }
+
+ unlink("${fileNewer}.orig");
+ unlink("${fileNewer}.rej");
+
+ open(PATCH, "| patch --force --fuzz=3 --binary \"$fileNewer\" > " . File::Spec->devnull()) or die $!;
+ if ($traditionalReject) {
+ print PATCH $patch;
+ } else {
+ my $changeLogHash = fixChangeLogPatch($patch);
+ print PATCH $changeLogHash->{patch};
+ }
+ close(PATCH);
+
+ my $result = !exitStatus($?);
+
+ # Refuse to merge the patch if it did not apply cleanly
+ if (-e "${fileNewer}.rej") {
+ unlink("${fileNewer}.rej");
+ if (-f "${fileNewer}.orig") {
+ unlink($fileNewer);
+ rename("${fileNewer}.orig", $fileNewer);
+ }
+ } else {
+ unlink("${fileNewer}.orig");
+ }
+
+ if ($traditionalReject) {
+ rename("$fileMine.save", $fileMine);
+ rename("$fileOlder.save", $fileOlder);
+ }
+
+ return $result;
+}
+
+sub gitConfig($)
+{
+ return unless isGit();
+
+ my ($config) = @_;
+
+ my $result = `git config $config`;
+ chomp $result;
+ return $result;
+}
+
+sub changeLogNameError($)
+{
+ my ($message) = @_;
+ print STDERR "$message\nEither:\n";
+ print STDERR " set CHANGE_LOG_NAME in your environment\n";
+ print STDERR " OR pass --name= on the command line\n";
+ print STDERR " OR set REAL_NAME in your environment";
+ print STDERR " OR git users can set 'git config user.name'\n";
+ exit(1);
+}
+
+sub changeLogName()
+{
+ my $name = $ENV{CHANGE_LOG_NAME} || $ENV{REAL_NAME} || gitConfig("user.name") || (split /\s*,\s*/, (getpwuid $<)[6])[0];
+
+ changeLogNameError("Failed to determine ChangeLog name.") unless $name;
+ # getpwuid seems to always succeed on windows, returning the username instead of the full name. This check will catch that case.
+ changeLogNameError("'$name' does not contain a space! ChangeLogs should contain your full name.") unless ($name =~ /\S\s\S/);
+
+ return $name;
+}
+
+sub changeLogEmailAddressError($)
+{
+ my ($message) = @_;
+ print STDERR "$message\nEither:\n";
+ print STDERR " set CHANGE_LOG_EMAIL_ADDRESS in your environment\n";
+ print STDERR " OR pass --email= on the command line\n";
+ print STDERR " OR set EMAIL_ADDRESS in your environment\n";
+ print STDERR " OR git users can set 'git config user.email'\n";
+ exit(1);
+}
+
+sub changeLogEmailAddress()
+{
+ my $emailAddress = $ENV{CHANGE_LOG_EMAIL_ADDRESS} || $ENV{EMAIL_ADDRESS} || gitConfig("user.email");
+
+ changeLogEmailAddressError("Failed to determine email address for ChangeLog.") unless $emailAddress;
+ changeLogEmailAddressError("Email address '$emailAddress' does not contain '\@' and is likely invalid.") unless ($emailAddress =~ /\@/);
+
+ return $emailAddress;
+}
+
+# http://tools.ietf.org/html/rfc1924
+sub decodeBase85($)
+{
+ my ($encoded) = @_;
+ my %table;
+ my @characters = ('0'..'9', 'A'..'Z', 'a'..'z', '!', '#', '$', '%', '&', '(', ')', '*', '+', '-', ';', '<', '=', '>', '?', '@', '^', '_', '`', '{', '|', '}', '~');
+ for (my $i = 0; $i < 85; $i++) {
+ $table{$characters[$i]} = $i;
+ }
+
+ my $decoded = '';
+ my @encodedChars = $encoded =~ /./g;
+
+ for (my $encodedIter = 0; defined($encodedChars[$encodedIter]);) {
+ my $digit = 0;
+ for (my $i = 0; $i < 5; $i++) {
+ $digit *= 85;
+ my $char = $encodedChars[$encodedIter];
+ $digit += $table{$char};
+ $encodedIter++;
+ }
+
+ for (my $i = 0; $i < 4; $i++) {
+ $decoded .= chr(($digit >> (3 - $i) * 8) & 255);
+ }
+ }
+
+ return $decoded;
+}
+
+sub decodeGitBinaryChunk($$)
+{
+ my ($contents, $fullPath) = @_;
+
+ # Load this module lazily in case the user don't have this module
+ # and won't handle git binary patches.
+ require Compress::Zlib;
+
+ my $encoded = "";
+ my $compressedSize = 0;
+ while ($contents =~ /^([A-Za-z])(.*)$/gm) {
+ my $line = $2;
+ next if $line eq "";
+ die "$fullPath: unexpected size of a line: $&" if length($2) % 5 != 0;
+ my $actualSize = length($2) / 5 * 4;
+ my $encodedExpectedSize = ord($1);
+ my $expectedSize = $encodedExpectedSize <= ord("Z") ? $encodedExpectedSize - ord("A") + 1 : $encodedExpectedSize - ord("a") + 27;
+
+ die "$fullPath: unexpected size of a line: $&" if int(($expectedSize + 3) / 4) * 4 != $actualSize;
+ $compressedSize += $expectedSize;
+ $encoded .= $line;
+ }
+
+ my $compressed = decodeBase85($encoded);
+ $compressed = substr($compressed, 0, $compressedSize);
+ return Compress::Zlib::uncompress($compressed);
+}
+
+sub decodeGitBinaryPatch($$)
+{
+ my ($contents, $fullPath) = @_;
+
+ # Git binary patch has two chunks. One is for the normal patching
+ # and another is for the reverse patching.
+ #
+ # Each chunk a line which starts from either "literal" or "delta",
+ # followed by a number which specifies decoded size of the chunk.
+ #
+ # Then, content of the chunk comes. To decode the content, we
+ # need decode it with base85 first, and then zlib.
+ my $gitPatchRegExp = '(literal|delta) ([0-9]+)\n([A-Za-z0-9!#$%&()*+-;<=>?@^_`{|}~\\n]*?)\n\n';
+ if ($contents !~ m"\nGIT binary patch\n$gitPatchRegExp$gitPatchRegExp\Z") {
+ die "$fullPath: unknown git binary patch format"
+ }
+
+ my $binaryChunkType = $1;
+ my $binaryChunkExpectedSize = $2;
+ my $encodedChunk = $3;
+ my $reverseBinaryChunkType = $4;
+ my $reverseBinaryChunkExpectedSize = $5;
+ my $encodedReverseChunk = $6;
+
+ my $binaryChunk = decodeGitBinaryChunk($encodedChunk, $fullPath);
+ my $binaryChunkActualSize = length($binaryChunk);
+ my $reverseBinaryChunk = decodeGitBinaryChunk($encodedReverseChunk, $fullPath);
+ my $reverseBinaryChunkActualSize = length($reverseBinaryChunk);
+
+ die "$fullPath: unexpected size of the first chunk (expected $binaryChunkExpectedSize but was $binaryChunkActualSize" if ($binaryChunkType eq "literal" and $binaryChunkExpectedSize != $binaryChunkActualSize);
+ die "$fullPath: unexpected size of the second chunk (expected $reverseBinaryChunkExpectedSize but was $reverseBinaryChunkActualSize" if ($reverseBinaryChunkType eq "literal" and $reverseBinaryChunkExpectedSize != $reverseBinaryChunkActualSize);
+
+ return ($binaryChunkType, $binaryChunk, $reverseBinaryChunkType, $reverseBinaryChunk);
+}
+
+sub readByte($$)
+{
+ my ($data, $location) = @_;
+
+ # Return the byte at $location in $data as a numeric value.
+ return ord(substr($data, $location, 1));
+}
+
+# The git binary delta format is undocumented, except in code:
+# - https://github.com/git/git/blob/master/delta.h:get_delta_hdr_size is the source
+# of the algorithm in decodeGitBinaryPatchDeltaSize.
+# - https://github.com/git/git/blob/master/patch-delta.c:patch_delta is the source
+# of the algorithm in applyGitBinaryPatchDelta.
+sub decodeGitBinaryPatchDeltaSize($)
+{
+ my ($binaryChunk) = @_;
+
+ # Source and destination buffer sizes are stored in 7-bit chunks at the
+ # start of the binary delta patch data. The highest bit in each byte
+ # except the last is set; the remaining 7 bits provide the next
+ # chunk of the size. The chunks are stored in ascending significance
+ # order.
+ my $cmd;
+ my $size = 0;
+ my $shift = 0;
+ for (my $i = 0; $i < length($binaryChunk);) {
+ $cmd = readByte($binaryChunk, $i++);
+ $size |= ($cmd & 0x7f) << $shift;
+ $shift += 7;
+ if (!($cmd & 0x80)) {
+ return ($size, $i);
+ }
+ }
+}
+
+sub applyGitBinaryPatchDelta($$)
+{
+ my ($binaryChunk, $originalContents) = @_;
+
+ # Git delta format consists of two headers indicating source buffer size
+ # and result size, then a series of commands. Each command is either
+ # a copy-from-old-version (the 0x80 bit is set) or a copy-from-delta
+ # command. Commands are applied sequentially to generate the result.
+ #
+ # A copy-from-old-version command encodes an offset and size to copy
+ # from in subsequent bits, while a copy-from-delta command consists only
+ # of the number of bytes to copy from the delta.
+
+ # We don't use these values, but we need to know how big they are so that
+ # we can skip to the diff data.
+ my ($size, $bytesUsed) = decodeGitBinaryPatchDeltaSize($binaryChunk);
+ $binaryChunk = substr($binaryChunk, $bytesUsed);
+ ($size, $bytesUsed) = decodeGitBinaryPatchDeltaSize($binaryChunk);
+ $binaryChunk = substr($binaryChunk, $bytesUsed);
+
+ my $out = "";
+ for (my $i = 0; $i < length($binaryChunk); ) {
+ my $cmd = ord(substr($binaryChunk, $i++, 1));
+ if ($cmd & 0x80) {
+ # Extract an offset and size from the delta data, then copy
+ # $size bytes from $offset in the original data into the output.
+ my $offset = 0;
+ my $size = 0;
+ if ($cmd & 0x01) { $offset = readByte($binaryChunk, $i++); }
+ if ($cmd & 0x02) { $offset |= readByte($binaryChunk, $i++) << 8; }
+ if ($cmd & 0x04) { $offset |= readByte($binaryChunk, $i++) << 16; }
+ if ($cmd & 0x08) { $offset |= readByte($binaryChunk, $i++) << 24; }
+ if ($cmd & 0x10) { $size = readByte($binaryChunk, $i++); }
+ if ($cmd & 0x20) { $size |= readByte($binaryChunk, $i++) << 8; }
+ if ($cmd & 0x40) { $size |= readByte($binaryChunk, $i++) << 16; }
+ if ($size == 0) { $size = 0x10000; }
+ $out .= substr($originalContents, $offset, $size);
+ } elsif ($cmd) {
+ # Copy $cmd bytes from the delta data into the output.
+ $out .= substr($binaryChunk, $i, $cmd);
+ $i += $cmd;
+ } else {
+ die "unexpected delta opcode 0";
+ }
+ }
+
+ return $out;
+}
+
+sub escapeSubversionPath($)
+{
+ my ($path) = @_;
+ $path .= "@" if $path =~ /@/;
+ return $path;
+}
+
+sub runCommand(@)
+{
+ my @args = @_;
+ my $pid = open(CHILD, "-|");
+ if (!defined($pid)) {
+ die "Failed to fork(): $!";
+ }
+ if ($pid) {
+ # Parent process
+ my $childStdout;
+ while (<CHILD>) {
+ $childStdout .= $_;
+ }
+ close(CHILD);
+ my %childOutput;
+ $childOutput{exitStatus} = exitStatus($?);
+ $childOutput{stdout} = $childStdout if $childStdout;
+ return \%childOutput;
+ }
+ # Child process
+ # FIXME: Consider further hardening of this function, including sanitizing the environment.
+ exec { $args[0] } @args or die "Failed to exec(): $!";
+}
+
+sub gitCommitForSVNRevision
+{
+ my ($svnRevision) = @_;
+ my $command = "git svn find-rev r" . $svnRevision;
+ $command = "LC_ALL=C $command" if !isWindows();
+ my $gitHash = `$command`;
+ if (!defined($gitHash)) {
+ $gitHash = "unknown";
+ warn "Unable to determine GIT commit from SVN revision";
+ } else {
+ chop($gitHash);
+ }
+ return $gitHash;
+}
+
+sub listOfChangedFilesBetweenRevisions
+{
+ my ($sourceDir, $firstRevision, $lastRevision) = @_;
+ my $command;
+
+ if ($firstRevision eq "unknown" or $lastRevision eq "unknown") {
+ return ();
+ }
+
+ # Some VCS functions don't work from within the build dir, so always
+ # go to the source dir first.
+ my $cwd = Cwd::getcwd();
+ chdir $sourceDir;
+
+ if (isGit()) {
+ my $firstCommit = gitCommitForSVNRevision($firstRevision);
+ my $lastCommit = gitCommitForSVNRevision($lastRevision);
+ $command = "git diff --name-status $firstCommit..$lastCommit";
+ } elsif (isSVN()) {
+ $command = "svn diff --summarize -r $firstRevision:$lastRevision";
+ }
+
+ my @result = ();
+
+ if ($command) {
+ my $diffOutput = `$command`;
+ $diffOutput =~ s/^[A-Z]\s+//gm;
+ @result = split(/[\r\n]+/, $diffOutput);
+ }
+
+ chdir $cwd;
+
+ return @result;
+}
+
+
+1;
diff --git a/Tools/Scripts/run-gtk-tests b/Tools/Scripts/run-gtk-tests
new file mode 100755
index 000000000..da98be042
--- /dev/null
+++ b/Tools/Scripts/run-gtk-tests
@@ -0,0 +1,461 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2011, 2012 Igalia S.L.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public License
+# along with this library; see the file COPYING.LIB. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+import subprocess
+import os
+import sys
+import optparse
+import re
+from signal import alarm, signal, SIGALRM, SIGKILL, SIGSEGV
+from gi.repository import Gio, GLib
+
+top_level_directory = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))
+sys.path.append(os.path.join(top_level_directory, "Tools", "jhbuild"))
+sys.path.append(os.path.join(top_level_directory, "Tools", "gtk"))
+import common
+import jhbuildutils
+
+class SkippedTest:
+ ENTIRE_SUITE = None
+
+ def __init__(self, test, test_case, reason, bug=None):
+ self.test = test
+ self.test_case = test_case
+ self.reason = reason
+ self.bug = bug
+
+ def __str__(self):
+ skipped_test_str = "%s" % self.test
+
+ if not(self.skip_entire_suite()):
+ skipped_test_str += " [%s]" % self.test_case
+
+ skipped_test_str += ": %s " % self.reason
+ if self.bug is not None:
+ skipped_test_str += "(https://bugs.webkit.org/show_bug.cgi?id=%d)" % self.bug
+ return skipped_test_str
+
+ def skip_entire_suite(self):
+ return self.test_case == SkippedTest.ENTIRE_SUITE
+
+class TestTimeout(Exception):
+ pass
+
+class TestRunner:
+ TEST_DIRS = [ "WebKit2Gtk", "WebKit2", "JavaScriptCore", "WTF", "WebCore" ]
+
+ SKIPPED = [
+ SkippedTest("WebKit2Gtk/TestUIClient", "/webkit2/WebKitWebView/mouse-target", "Test times out after r150890", 117689),
+ SkippedTest("WebKit2Gtk/TestCookieManager", "/webkit2/WebKitCookieManager/persistent-storage", "Test is flaky", 134580),
+ SkippedTest("WebKit2Gtk/TestWebExtensions", "/webkit2/WebKitWebView/install-missing-plugins-permission-request", "Test times out", 147822),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.MouseMoveAfterCrash", "Test is flaky", 85066),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.NewFirstVisuallyNonEmptyLayoutForImages", "Test is flaky", 85066),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.NewFirstVisuallyNonEmptyLayoutFrames", "Test fails", 85037),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.RestoreSessionStateContainingFormData", "Session State is not implemented in GTK+ port", 84960),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.SpacebarScrolling", "Test fails", 84961),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.WKConnection", "Tests fail and time out out", 84959),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.WKPageGetScaleFactorNotZero", "Test fails and times out", 88455),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.ForceRepaint", "Test times out", 105532),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.ReloadPageAfterCrash", "Test flakily times out", 110129),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.DidAssociateFormControls", "Test times out", 120302),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.InjectedBundleFrameHitTest", "Test times out", 120303),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.TerminateTwice", "Test causes crash on the next test", 121970),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.GeolocationTransitionToHighAccuracy", "Test causes crash on the next test", 125068),
+ SkippedTest("WebKit2/TestWebKit2", "WebKit2.GeolocationTransitionToLowAccuracy", "Test causes crash on the next test", 125068),
+ ]
+
+ SLOW = [
+ "WTF_Lock.ContendedShortSection",
+ ]
+
+ def __init__(self, options, tests=[]):
+ self._options = options
+
+ self._build_type = "Debug" if self._options.debug else "Release"
+ common.set_build_types((self._build_type,))
+
+ self._programs_path = common.binary_build_path()
+ self._tests = self._get_tests(tests)
+ self._skipped_tests = TestRunner.SKIPPED
+ self._disabled_tests = []
+
+ # These SPI daemons need to be active for the accessibility tests to work.
+ self._spi_registryd = None
+ self._spi_bus_launcher = None
+
+ def _test_programs_base_dir(self):
+ return os.path.join(self._programs_path, "TestWebKitAPI")
+
+ def _get_tests_from_dir(self, test_dir):
+ if not os.path.isdir(test_dir):
+ return []
+
+ tests = []
+ for test_file in os.listdir(test_dir):
+ if not test_file.lower().startswith("test"):
+ continue
+ test_path = os.path.join(test_dir, test_file)
+ if os.path.isfile(test_path) and os.access(test_path, os.X_OK):
+ tests.append(test_path)
+ return tests
+
+ def _get_tests(self, initial_tests):
+ tests = []
+ for test in initial_tests:
+ if os.path.isdir(test):
+ tests.extend(self._get_tests_from_dir(test))
+ else:
+ tests.append(test)
+ if tests:
+ return tests
+
+ tests = []
+ for test_dir in self.TEST_DIRS:
+ absolute_test_dir = os.path.join(self._test_programs_base_dir(), test_dir)
+ tests.extend(self._get_tests_from_dir(absolute_test_dir))
+ return tests
+
+ def _lookup_atspi2_binary(self, filename):
+ exec_prefix = common.pkg_config_file_variable('atspi-2', 'exec_prefix')
+ if not exec_prefix:
+ return None
+ for path in ['libexec', 'lib/at-spi2-core', 'lib32/at-spi2-core', 'lib64/at-spi2-core']:
+ filepath = os.path.join(exec_prefix, path, filename)
+ if os.path.isfile(filepath):
+ return filepath
+
+ return None
+
+ def _start_accessibility_daemons(self):
+ spi_bus_launcher_path = self._lookup_atspi2_binary('at-spi-bus-launcher')
+ spi_registryd_path = self._lookup_atspi2_binary('at-spi2-registryd')
+ if not spi_bus_launcher_path or not spi_registryd_path:
+ return False
+
+ try:
+ self._spi_bus_launcher = subprocess.Popen([spi_bus_launcher_path], env=self._test_env)
+ except:
+ sys.stderr.write("Failed to launch the accessibility bus\n")
+ sys.stderr.flush()
+ return False
+
+ # We need to wait until the SPI bus is launched before trying to start the SPI
+ # registry, so we spin a main loop until the bus name appears on DBus.
+ loop = GLib.MainLoop()
+ Gio.bus_watch_name(Gio.BusType.SESSION, 'org.a11y.Bus', Gio.BusNameWatcherFlags.NONE,
+ lambda *args: loop.quit(), None)
+ loop.run()
+
+ try:
+ self._spi_registryd = subprocess.Popen([spi_registryd_path], env=self._test_env)
+ except:
+ sys.stderr.write("Failed to launch the accessibility registry\n")
+ sys.stderr.flush()
+ return False
+
+ return True
+
+ def _run_xvfb(self):
+ self._xvfb = None
+ if not self._options.use_xvfb:
+ return True
+
+ self._test_env["DISPLAY"] = self._options.display
+
+ try:
+ self._xvfb = subprocess.Popen(["Xvfb", self._options.display, "-screen", "0", "800x600x24", "-nolisten", "tcp"],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except Exception as e:
+ sys.stderr.write("Failed to run Xvfb: %s\n" % e)
+ sys.stderr.flush()
+ return False
+
+ return True
+
+ def _setup_testing_environment(self):
+ self._test_env = os.environ
+ self._test_env['GSETTINGS_BACKEND'] = 'memory'
+ self._test_env["TEST_WEBKIT_API_WEBKIT2_RESOURCES_PATH"] = common.top_level_path("Tools", "TestWebKitAPI", "Tests", "WebKit2")
+ self._test_env["TEST_WEBKIT_API_WEBKIT2_INJECTED_BUNDLE_PATH"] = common.library_build_path()
+ self._test_env["WEBKIT_EXEC_PATH"] = self._programs_path
+
+ if not self._run_xvfb():
+ return False
+
+ # If we cannot start the accessibility daemons, we can just skip the accessibility tests.
+ if not self._start_accessibility_daemons():
+ print "Could not start accessibility bus, so disabling TestWebKitAccessibility"
+ self._disabled_tests.append("WebKit2APITests/TestWebKitAccessibility")
+ return True
+
+ def _tear_down_testing_environment(self):
+ if self._spi_registryd:
+ self._spi_registryd.terminate()
+ if self._spi_bus_launcher:
+ self._spi_bus_launcher.terminate()
+ if self._xvfb:
+ self._xvfb.terminate()
+
+ def _test_cases_to_skip(self, test_program):
+ if self._options.skipped_action != 'skip':
+ return []
+
+ test_cases = []
+ for skipped in self._skipped_tests:
+ if test_program.endswith(skipped.test) and not skipped.skip_entire_suite():
+ test_cases.append(skipped.test_case)
+ return test_cases
+
+ def _should_run_test_program(self, test_program):
+ for disabled_test in self._disabled_tests:
+ if test_program.endswith(disabled_test):
+ return False
+
+ if self._options.skipped_action != 'skip':
+ return True
+
+ for skipped in self._skipped_tests:
+ if test_program.endswith(skipped.test) and skipped.skip_entire_suite():
+ return False
+ return True
+
+ def _get_child_pid_from_test_output(self, output):
+ if not output:
+ return -1
+ match = re.search(r'\(pid=(?P<child_pid>[0-9]+)\)', output)
+ if not match:
+ return -1
+ return int(match.group('child_pid'))
+
+ def _kill_process(self, pid):
+ try:
+ os.kill(pid, SIGKILL)
+ except OSError:
+ # Process already died.
+ pass
+
+ def _run_test_command(self, command, timeout=-1):
+ def alarm_handler(signum, frame):
+ raise TestTimeout
+
+ child_pid = [-1]
+ def parse_line(line, child_pid = child_pid):
+ if child_pid[0] == -1:
+ child_pid[0] = self._get_child_pid_from_test_output(line)
+
+ sys.stdout.write(line)
+
+ def waitpid(pid):
+ while True:
+ try:
+ return os.waitpid(pid, 0)
+ except (OSError, IOError) as e:
+ if e.errno == errno.EINTR:
+ continue
+ raise
+
+ def return_code_from_exit_status(status):
+ if os.WIFSIGNALED(status):
+ return -os.WTERMSIG(status)
+ elif os.WIFEXITED(status):
+ return os.WEXITSTATUS(status)
+ else:
+ # Should never happen
+ raise RuntimeError("Unknown child exit status!")
+
+ pid, fd = os.forkpty()
+ if pid == 0:
+ os.execvpe(command[0], command, self._test_env)
+ sys.exit(0)
+
+ if timeout > 0:
+ signal(SIGALRM, alarm_handler)
+ alarm(timeout)
+
+ try:
+ common.parse_output_lines(fd, parse_line)
+ if timeout > 0:
+ alarm(0)
+ except TestTimeout:
+ self._kill_process(pid)
+ if child_pid[0] > 0:
+ self._kill_process(child_pid[0])
+ raise
+
+ try:
+ dummy, status = waitpid(pid)
+ except OSError as e:
+ if e.errno != errno.ECHILD:
+ raise
+ # This happens if SIGCLD is set to be ignored or waiting
+ # for child processes has otherwise been disabled for our
+ # process. This child is dead, we can't get the status.
+ status = 0
+
+ return return_code_from_exit_status(status)
+
+ def _run_test_glib(self, test_program):
+ tester_command = ['gtester', '-k']
+ if self._options.verbose:
+ tester_command.append('--verbose')
+ for test_case in self._test_cases_to_skip(test_program):
+ tester_command.extend(['-s', test_case])
+ tester_command.append(test_program)
+
+ return self._run_test_command(tester_command, self._options.timeout)
+
+ def _get_tests_from_google_test_suite(self, test_program):
+ try:
+ output = subprocess.check_output([test_program, '--gtest_list_tests'])
+ except subprocess.CalledProcessError:
+ sys.stderr.write("ERROR: could not list available tests for binary %s.\n" % (test_program))
+ sys.stderr.flush()
+ return 1
+
+ skipped_test_cases = self._test_cases_to_skip(test_program)
+
+ tests = []
+ prefix = None
+ for line in output.split('\n'):
+ if not line.startswith(' '):
+ prefix = line
+ continue
+ else:
+ test_name = prefix + line.strip()
+ if not test_name in skipped_test_cases:
+ tests.append(test_name)
+ return tests
+
+ def _run_google_test(self, test_program, subtest):
+ test_command = [test_program, '--gtest_filter=%s' % (subtest)]
+ timeout = self._options.timeout
+ if subtest in TestRunner.SLOW:
+ timeout *= 5
+
+ status = self._run_test_command(test_command, timeout)
+ if status == -SIGSEGV:
+ sys.stdout.write("**CRASH** %s\n" % subtest)
+ sys.stdout.flush()
+ return status
+
+ def _run_google_test_suite(self, test_program):
+ retcode = 0
+ for subtest in self._get_tests_from_google_test_suite(test_program):
+ if self._run_google_test(test_program, subtest):
+ retcode = 1
+ return retcode
+
+ def _run_test(self, test_program):
+ basedir = os.path.basename(os.path.dirname(test_program))
+ if basedir in ["WebKit2Gtk", "WebKitGtk"]:
+ return self._run_test_glib(test_program)
+
+ if basedir in ["WebKit2", "JavaScriptCore", "WTF", "WebCore", "WebCoreGtk"]:
+ return self._run_google_test_suite(test_program)
+
+ return 1
+
+ def run_tests(self):
+ if not self._tests:
+ sys.stderr.write("ERROR: tests not found in %s.\n" % (self._test_programs_base_dir()))
+ sys.stderr.flush()
+ return 1
+
+ if not self._setup_testing_environment():
+ return 1
+
+ # Remove skipped tests now instead of when we find them, because
+ # some tests might be skipped while setting up the test environment.
+ self._tests = [test for test in self._tests if self._should_run_test_program(test)]
+
+ crashed_tests = []
+ failed_tests = []
+ timed_out_tests = []
+ try:
+ for test in self._tests:
+ exit_status_code = 0
+ try:
+ exit_status_code = self._run_test(test)
+ except TestTimeout:
+ sys.stdout.write("TEST: %s: TIMEOUT\n" % test)
+ sys.stdout.flush()
+ timed_out_tests.append(test)
+
+ if exit_status_code == -SIGSEGV:
+ sys.stdout.write("TEST: %s: CRASHED\n" % test)
+ sys.stdout.flush()
+ crashed_tests.append(test)
+ elif exit_status_code != 0:
+ failed_tests.append(test)
+ finally:
+ self._tear_down_testing_environment()
+
+ if failed_tests:
+ names = [test.replace(self._test_programs_base_dir(), '', 1) for test in failed_tests]
+ sys.stdout.write("Tests failed (%d): %s\n" % (len(names), ", ".join(names)))
+ sys.stdout.flush()
+
+ if crashed_tests:
+ names = [test.replace(self._test_programs_base_dir(), '', 1) for test in crashed_tests]
+ sys.stdout.write("Tests that crashed (%d): %s\n" % (len(names), ", ".join(names)))
+ sys.stdout.flush()
+
+ if timed_out_tests:
+ names = [test.replace(self._test_programs_base_dir(), '', 1) for test in timed_out_tests]
+ sys.stdout.write("Tests that timed out (%d): %s\n" % (len(names), ", ".join(names)))
+ sys.stdout.flush()
+
+ if self._skipped_tests and self._options.skipped_action == 'skip':
+ sys.stdout.write("Tests skipped (%d):\n%s\n" %
+ (len(self._skipped_tests),
+ "\n".join([str(skipped) for skipped in self._skipped_tests])))
+ sys.stdout.flush()
+
+ return len(failed_tests) + len(timed_out_tests)
+
+if __name__ == "__main__":
+ if not jhbuildutils.enter_jhbuild_environment_if_available("gtk"):
+ print "***"
+ print "*** Warning: jhbuild environment not present. Run update-webkitgtk-libs before build-webkit to ensure proper testing."
+ print "***"
+
+ option_parser = optparse.OptionParser(usage='usage: %prog [options] [test...]')
+ option_parser.add_option('-r', '--release',
+ action='store_true', dest='release',
+ help='Run in Release')
+ option_parser.add_option('-d', '--debug',
+ action='store_true', dest='debug',
+ help='Run in Debug')
+ option_parser.add_option('-v', '--verbose',
+ action='store_true', dest='verbose',
+ help='Run gtester in verbose mode')
+ option_parser.add_option('--display', action='store', dest='display', default=':55',
+ help='Display to run Xvfb')
+ option_parser.add_option('--skipped', action='store', dest='skipped_action',
+ choices=['skip', 'ignore', 'only'], default='skip',
+ metavar='skip|ignore|only',
+ help='Specifies how to treat the skipped tests')
+ option_parser.add_option('-t', '--timeout',
+ action='store', type='int', dest='timeout', default=10,
+ help='Time in seconds until a test times out')
+ option_parser.add_option('--no-xvfb', action='store_false', dest='use_xvfb', default=True,
+ help='Do not run tests under Xvfb')
+ options, args = option_parser.parse_args()
+
+ sys.exit(TestRunner(options, args).run_tests())
diff --git a/Tools/Scripts/webkit-build-directory b/Tools/Scripts/webkit-build-directory
new file mode 100755
index 000000000..75a00fffb
--- /dev/null
+++ b/Tools/Scripts/webkit-build-directory
@@ -0,0 +1,79 @@
+#!/usr/bin/perl -w
+
+# Copyright (C) 2010 Google Inc. All rights reserved.
+# Copyright (C) 2013 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of Apple Inc. ("Apple") nor the names of
+# its contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# A script to expose WebKit's build directory detection logic to non-perl scripts.
+
+use FindBin;
+use Getopt::Long;
+
+use lib $FindBin::Bin;
+use webkitdirs;
+
+my $showConfigurationDirectory = 0;
+my $showHelp = 0;
+my $showTopLevelDirectory = 0;
+
+
+my $programName = basename($0);
+my $usage = <<EOF;
+Usage: $programName [options]
+ --configuration Show the build directory for a specific configuration (e.g. Debug, Release. Defaults to the active configuration set by set-webkit-configuration)
+ -h|--help Show this help message
+ --top-level Show the top-level build directory
+
+ --efl Find the build directory for the EFL port
+ --gtk Find the build directory for the GTK+ port
+ --wincairo Find the build directory for using Cairo (rather than CoreGraphics) on Windows
+
+Either --configuration or --top-level is required.
+EOF
+
+setConfiguration(); # Figure out from the command line if we're --debug or --release or the default.
+
+# FIXME: Check if extra flags are valid or not.
+Getopt::Long::Configure('pass_through'); # Let --blackberry, etc... be handled by webkitdirs
+my $getOptionsResult = GetOptions(
+ 'configuration' => \$showConfigurationDirectory,
+ 'top-level' => \$showTopLevelDirectory,
+ 'help|h' => \$showHelp,
+);
+
+if (!$getOptionsResult || $showHelp) {
+ print STDERR $usage;
+ exit 1;
+}
+
+if (!$showConfigurationDirectory && !$showTopLevelDirectory) {
+ print baseProductDir() . "\n";
+ print productDir() . "\n";
+} elsif ($showTopLevelDirectory) {
+ print baseProductDir() . "\n";
+} else {
+ print productDir() . "\n";
+}
diff --git a/Tools/Scripts/webkitdirs.pm b/Tools/Scripts/webkitdirs.pm
new file mode 100755
index 000000000..dcb89eb7d
--- /dev/null
+++ b/Tools/Scripts/webkitdirs.pm
@@ -0,0 +1,2505 @@
+# Copyright (C) 2005-2007, 2010-2014 Apple Inc. All rights reserved.
+# Copyright (C) 2009 Google Inc. All rights reserved.
+# Copyright (C) 2011 Research In Motion Limited. All rights reserved.
+# Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of Apple Inc. ("Apple") nor the names of
+# its contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Module to share code to get to WebKit directories.
+
+use strict;
+use version;
+use warnings;
+use Config;
+use Cwd qw(realpath);
+use Digest::MD5 qw(md5_hex);
+use FindBin;
+use File::Basename;
+use File::Find;
+use File::Path qw(make_path mkpath rmtree);
+use File::Spec;
+use File::stat;
+use List::Util;
+use POSIX;
+use Time::HiRes qw(usleep);
+use VCSUtils;
+
+BEGIN {
+ use Exporter ();
+ our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
+ $VERSION = 1.00;
+ @ISA = qw(Exporter);
+ @EXPORT = qw(
+ &XcodeCoverageSupportOptions
+ &XcodeOptionString
+ &XcodeOptionStringNoConfig
+ &XcodeOptions
+ &XcodeStaticAnalyzerOption
+ &appDisplayNameFromBundle
+ &baseProductDir
+ &chdirWebKit
+ &checkFrameworks
+ &cmakeBasedPortArguments
+ &cmakeBasedPortName
+ &currentSVNRevision
+ &debugSafari
+ &findOrCreateSimulatorForIOSDevice
+ &iosSimulatorDeviceByName
+ &nmPath
+ &passedConfiguration
+ &printHelpAndExitForRunAndDebugWebKitAppIfNeeded
+ &productDir
+ &quitIOSSimulator
+ &relaunchIOSSimulator
+ &runIOSWebKitApp
+ &runMacWebKitApp
+ &safariPath
+ &setConfiguration
+ &setupMacWebKitEnvironment
+ &sharedCommandLineOptions
+ &sharedCommandLineOptionsUsage
+ USE_OPEN_COMMAND
+ );
+ %EXPORT_TAGS = ( );
+ @EXPORT_OK = ();
+}
+
+use constant USE_OPEN_COMMAND => 1; # Used in runMacWebKitApp().
+use constant INCLUDE_OPTIONS_FOR_DEBUGGING => 1;
+use constant SIMULATOR_DEVICE_STATE_SHUTDOWN => "1";
+use constant SIMULATOR_DEVICE_STATE_BOOTED => "3";
+
+our @EXPORT_OK;
+
+my $architecture;
+my $asanIsEnabled;
+my $numberOfCPUs;
+my $maxCPULoad;
+my $baseProductDir;
+my @baseProductDirOption;
+my $configuration;
+my $xcodeSDK;
+my $configurationForVisualStudio;
+my $configurationProductDir;
+my $sourceDir;
+my $currentSVNRevision;
+my $debugger;
+my $didLoadIPhoneSimulatorNotification;
+my $nmPath;
+my $osXVersion;
+my $generateDsym;
+my $isGtk;
+my $isWinCairo;
+my $isWin64;
+my $isEfl;
+my $isInspectorFrontend;
+my $shouldTargetWebProcess;
+my $shouldUseXPCServiceForWebProcess;
+my $shouldUseGuardMalloc;
+my $xcodeVersion;
+
+# Variables for Win32 support
+my $programFilesPath;
+my $vcBuildPath;
+my $vsInstallDir;
+my $msBuildInstallDir;
+my $vsVersion;
+my $windowsSourceDir;
+my $winVersion;
+my $willUseVCExpressWhenBuilding = 0;
+
+# Defined in VCSUtils.
+sub exitStatus($);
+
+sub findMatchingArguments($$);
+sub hasArgument($$);
+
+sub determineSourceDir
+{
+ return if $sourceDir;
+ $sourceDir = $FindBin::Bin;
+ $sourceDir =~ s|/+$||; # Remove trailing '/' as we would die later
+
+ # walks up path checking each directory to see if it is the main WebKit project dir,
+ # defined by containing Sources, WebCore, and WebKit
+ until ((-d File::Spec->catdir($sourceDir, "Source") && -d File::Spec->catdir($sourceDir, "Source", "WebCore") && -d File::Spec->catdir($sourceDir, "Source", "WebKit")) || (-d File::Spec->catdir($sourceDir, "Internal") && -d File::Spec->catdir($sourceDir, "OpenSource")))
+ {
+ if ($sourceDir !~ s|/[^/]+$||) {
+ die "Could not find top level webkit directory above source directory using FindBin.\n";
+ }
+ }
+
+ $sourceDir = File::Spec->catdir($sourceDir, "OpenSource") if -d File::Spec->catdir($sourceDir, "OpenSource");
+}
+
+sub currentPerlPath()
+{
+ my $thisPerl = $^X;
+ if ($^O ne 'VMS') {
+ $thisPerl .= $Config{_exe} unless $thisPerl =~ m/$Config{_exe}$/i;
+ }
+ return $thisPerl;
+}
+
+# used for scripts which are stored in a non-standard location
+sub setSourceDir($)
+{
+ ($sourceDir) = @_;
+}
+
+sub determineNinjaVersion
+{
+ chomp(my $ninjaVersion = `ninja --version`);
+ return $ninjaVersion;
+}
+
+sub determineXcodeVersion
+{
+ return if defined $xcodeVersion;
+ my $xcodebuildVersionOutput = `xcodebuild -version`;
+ $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : "3.0";
+}
+
+sub readXcodeUserDefault($)
+{
+ my ($unprefixedKey) = @_;
+
+ determineXcodeVersion();
+
+ my $xcodeDefaultsDomain = (eval "v$xcodeVersion" lt v4) ? "com.apple.Xcode" : "com.apple.dt.Xcode";
+ my $xcodeDefaultsPrefix = (eval "v$xcodeVersion" lt v4) ? "PBX" : "IDE";
+ my $devnull = File::Spec->devnull();
+
+ my $value = `defaults read $xcodeDefaultsDomain ${xcodeDefaultsPrefix}${unprefixedKey} 2> ${devnull}`;
+ return if $?;
+
+ chomp $value;
+ return $value;
+}
+
+sub determineBaseProductDir
+{
+ return if defined $baseProductDir;
+ determineSourceDir();
+
+ my $setSharedPrecompsDir;
+ $baseProductDir = $ENV{"WEBKIT_OUTPUTDIR"};
+
+ if (!defined($baseProductDir) and isAppleMacWebKit()) {
+ # Silently remove ~/Library/Preferences/xcodebuild.plist which can
+ # cause build failure. The presence of
+ # ~/Library/Preferences/xcodebuild.plist can prevent xcodebuild from
+ # respecting global settings such as a custom build products directory
+ # (<rdar://problem/5585899>).
+ my $personalPlistFile = $ENV{HOME} . "/Library/Preferences/xcodebuild.plist";
+ if (-e $personalPlistFile) {
+ unlink($personalPlistFile) || die "Could not delete $personalPlistFile: $!";
+ }
+
+ determineXcodeVersion();
+
+ if (eval "v$xcodeVersion" ge v4) {
+ my $buildLocationStyle = join '', readXcodeUserDefault("BuildLocationStyle");
+ if ($buildLocationStyle eq "Custom") {
+ my $buildLocationType = join '', readXcodeUserDefault("CustomBuildLocationType");
+ # FIXME: Read CustomBuildIntermediatesPath and set OBJROOT accordingly.
+ $baseProductDir = readXcodeUserDefault("CustomBuildProductsPath") if $buildLocationType eq "Absolute";
+ }
+
+ # DeterminedByTargets corresponds to a setting of "Legacy" in Xcode.
+ # It is the only build location style for which SHARED_PRECOMPS_DIR is not
+ # overridden when building from within Xcode.
+ $setSharedPrecompsDir = 1 if $buildLocationStyle ne "DeterminedByTargets";
+ }
+
+ if (!defined($baseProductDir)) {
+ $baseProductDir = join '', readXcodeUserDefault("ApplicationwideBuildSettings");
+ $baseProductDir = $1 if $baseProductDir =~ /SYMROOT\s*=\s*\"(.*?)\";/s;
+ }
+
+ undef $baseProductDir unless $baseProductDir =~ /^\//;
+ }
+
+ if (!defined($baseProductDir)) { # Port-specific checks failed, use default
+ $baseProductDir = "$sourceDir/WebKitBuild";
+ }
+
+ if (isGit() && isGitBranchBuild()) {
+ my $branch = gitBranch();
+ $baseProductDir = "$baseProductDir/$branch";
+ }
+
+ if (isAppleMacWebKit()) {
+ $baseProductDir =~ s|^\Q$(SRCROOT)/..\E$|$sourceDir|;
+ $baseProductDir =~ s|^\Q$(SRCROOT)/../|$sourceDir/|;
+ $baseProductDir =~ s|^~/|$ENV{HOME}/|;
+ die "Can't handle Xcode product directory with a ~ in it.\n" if $baseProductDir =~ /~/;
+ die "Can't handle Xcode product directory with a variable in it.\n" if $baseProductDir =~ /\$/;
+ @baseProductDirOption = ("SYMROOT=$baseProductDir", "OBJROOT=$baseProductDir");
+ push(@baseProductDirOption, "SHARED_PRECOMPS_DIR=${baseProductDir}/PrecompiledHeaders") if $setSharedPrecompsDir;
+ }
+
+ if (isCygwin()) {
+ my $dosBuildPath = `cygpath --windows \"$baseProductDir\"`;
+ chomp $dosBuildPath;
+ $ENV{"WEBKIT_OUTPUTDIR"} = $dosBuildPath;
+ my $unixBuildPath = `cygpath --unix \"$baseProductDir\"`;
+ chomp $unixBuildPath;
+ $baseProductDir = $unixBuildPath;
+ }
+}
+
+sub setBaseProductDir($)
+{
+ ($baseProductDir) = @_;
+}
+
+sub determineConfiguration
+{
+ return if defined $configuration;
+ determineBaseProductDir();
+ if (open CONFIGURATION, "$baseProductDir/Configuration") {
+ $configuration = <CONFIGURATION>;
+ close CONFIGURATION;
+ }
+ if ($configuration) {
+ chomp $configuration;
+ # compatibility for people who have old Configuration files
+ $configuration = "Release" if $configuration eq "Deployment";
+ $configuration = "Debug" if $configuration eq "Development";
+ } else {
+ $configuration = "Release";
+ }
+}
+
+sub determineArchitecture
+{
+ return if defined $architecture;
+ # make sure $architecture is defined in all cases
+ $architecture = "";
+
+ determineBaseProductDir();
+ determineXcodeSDK();
+
+ if (isAppleMacWebKit()) {
+ if (open ARCHITECTURE, "$baseProductDir/Architecture") {
+ $architecture = <ARCHITECTURE>;
+ close ARCHITECTURE;
+ }
+ if ($architecture) {
+ chomp $architecture;
+ } else {
+ if (not defined $xcodeSDK or $xcodeSDK =~ /^(\/$|macosx)/) {
+ my $supports64Bit = `sysctl -n hw.optional.x86_64`;
+ chomp $supports64Bit;
+ $architecture = 'x86_64' if $supports64Bit;
+ } elsif ($xcodeSDK =~ /^iphonesimulator/) {
+ $architecture = 'x86_64';
+ } elsif ($xcodeSDK =~ /^iphoneos/) {
+ $architecture = 'armv7';
+ }
+ }
+ } elsif (isEfl() || isGtk()) {
+ my $host_processor = "";
+ $host_processor = `cmake --system-information | grep CMAKE_SYSTEM_PROCESSOR`;
+ if ($host_processor =~ m/^CMAKE_SYSTEM_PROCESSOR \"([^"]+)\"/) {
+ # We have a configured build tree; use it.
+ $architecture = $1;
+ $architecture = 'x86_64' if $architecture eq 'amd64';
+ }
+ }
+
+ if (!$architecture && (isGtk() || isAppleMacWebKit() || isEfl())) {
+ # Fall back to output of `arch', if it is present.
+ $architecture = `arch`;
+ chomp $architecture;
+ }
+
+ if (!$architecture && (isGtk() || isAppleMacWebKit() || isEfl())) {
+ # Fall back to output of `uname -m', if it is present.
+ $architecture = `uname -m`;
+ chomp $architecture;
+ }
+
+ $architecture = 'x86_64' if ($architecture =~ /amd64/ && isBSD());
+}
+
+sub determineASanIsEnabled
+{
+ return if defined $asanIsEnabled;
+ determineBaseProductDir();
+
+ $asanIsEnabled = 0;
+ my $asanConfigurationValue;
+
+ if (open ASAN, "$baseProductDir/ASan") {
+ $asanConfigurationValue = <ASAN>;
+ close ASAN;
+ chomp $asanConfigurationValue;
+ $asanIsEnabled = 1 if $asanConfigurationValue eq "YES";
+ }
+}
+
+sub determineNumberOfCPUs
+{
+ return if defined $numberOfCPUs;
+ if (defined($ENV{NUMBER_OF_PROCESSORS})) {
+ $numberOfCPUs = $ENV{NUMBER_OF_PROCESSORS};
+ } elsif (isLinux()) {
+ # First try the nproc utility, if it exists. If we get no
+ # results fall back to just interpretting /proc directly.
+ chomp($numberOfCPUs = `nproc --all 2> /dev/null`);
+ if ($numberOfCPUs eq "") {
+ $numberOfCPUs = (grep /processor/, `cat /proc/cpuinfo`);
+ }
+ } elsif (isWindows() || isCygwin()) {
+ # Assumes cygwin
+ $numberOfCPUs = `ls /proc/registry/HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/System/CentralProcessor | wc -w`;
+ } elsif (isDarwin() || isBSD()) {
+ chomp($numberOfCPUs = `sysctl -n hw.ncpu`);
+ }
+}
+
+sub determineMaxCPULoad
+{
+ return if defined $maxCPULoad;
+ if (defined($ENV{MAX_CPU_LOAD})) {
+ $maxCPULoad = $ENV{MAX_CPU_LOAD};
+ }
+}
+
+sub jscPath($)
+{
+ my ($productDir) = @_;
+ my $jscName = "jsc";
+ $jscName .= "_debug" if configuration() eq "Debug_All";
+ $jscName .= ".exe" if (isWindows() || isCygwin());
+ return "$productDir/$jscName" if -e "$productDir/$jscName";
+ return "$productDir/JavaScriptCore.framework/Resources/$jscName";
+}
+
+sub argumentsForConfiguration()
+{
+ determineConfiguration();
+ determineArchitecture();
+ determineXcodeSDK();
+
+ my @args = ();
+ # FIXME: Is it necessary to pass --debug, --release, --32-bit or --64-bit?
+ # These are determined automatically from stored configuration.
+ push(@args, '--debug') if ($configuration =~ "^Debug");
+ push(@args, '--release') if ($configuration =~ "^Release");
+ push(@args, '--device') if (defined $xcodeSDK && $xcodeSDK =~ /^iphoneos/);
+ push(@args, '--ios-simulator') if (defined $xcodeSDK && $xcodeSDK =~ /^iphonesimulator/);
+ push(@args, '--32-bit') if ($architecture ne "x86_64" and !isWin64());
+ push(@args, '--64-bit') if (isWin64());
+ push(@args, '--gtk') if isGtk();
+ push(@args, '--efl') if isEfl();
+ push(@args, '--wincairo') if isWinCairo();
+ push(@args, '--inspector-frontend') if isInspectorFrontend();
+ return @args;
+}
+
+sub determineXcodeSDK
+{
+ return if defined $xcodeSDK;
+ my $sdk;
+ if (checkForArgumentAndRemoveFromARGVGettingValue("--sdk", \$sdk)) {
+ $xcodeSDK = $sdk;
+ }
+ if (checkForArgumentAndRemoveFromARGV("--device")) {
+ my $hasInternalSDK = exitStatus(system("xcrun --sdk iphoneos.internal --show-sdk-version > /dev/null 2>&1")) == 0;
+ $xcodeSDK ||= $hasInternalSDK ? "iphoneos.internal" : "iphoneos";
+ }
+ if (checkForArgumentAndRemoveFromARGV("--ios-simulator")) {
+ $xcodeSDK ||= 'iphonesimulator';
+ }
+}
+
+sub xcodeSDK
+{
+ determineXcodeSDK();
+ return $xcodeSDK;
+}
+
+sub setXcodeSDK($)
+{
+ ($xcodeSDK) = @_;
+}
+
+
+sub xcodeSDKPlatformName()
+{
+ determineXcodeSDK();
+ return "" if !defined $xcodeSDK;
+ return "iphoneos" if $xcodeSDK =~ /iphoneos/i;
+ return "iphonesimulator" if $xcodeSDK =~ /iphonesimulator/i;
+ return "macosx" if $xcodeSDK =~ /macosx/i;
+ die "Couldn't determine platform name from Xcode SDK";
+}
+
+sub XcodeSDKPath
+{
+ determineXcodeSDK();
+
+ die "Can't find the SDK path because no Xcode SDK was specified" if not $xcodeSDK;
+
+ my $sdkPath = `xcrun --sdk $xcodeSDK --show-sdk-path` if $xcodeSDK;
+ die 'Failed to get SDK path from xcrun' if $?;
+ chomp $sdkPath;
+
+ return $sdkPath;
+}
+
+sub xcodeSDKVersion
+{
+ determineXcodeSDK();
+
+ die "Can't find the SDK version because no Xcode SDK was specified" if !$xcodeSDK;
+
+ chomp(my $sdkVersion = `xcrun --sdk $xcodeSDK --show-sdk-version`);
+ die "Failed to get SDK version from xcrun" if exitStatus($?);
+
+ return $sdkVersion;
+}
+
+sub programFilesPath
+{
+ return $programFilesPath if defined $programFilesPath;
+
+ $programFilesPath = $ENV{'PROGRAMFILES(X86)'} || $ENV{'PROGRAMFILES'} || "C:\\Program Files";
+
+ return $programFilesPath;
+}
+
+sub visualStudioInstallDir
+{
+ return $vsInstallDir if defined $vsInstallDir;
+
+ if ($ENV{'VSINSTALLDIR'}) {
+ $vsInstallDir = $ENV{'VSINSTALLDIR'};
+ $vsInstallDir =~ s|[\\/]$||;
+ } else {
+ $vsInstallDir = File::Spec->catdir(programFilesPath(), "Microsoft Visual Studio 14.0");
+ }
+ chomp($vsInstallDir = `cygpath "$vsInstallDir"`) if isCygwin();
+
+ print "Using Visual Studio: $vsInstallDir\n";
+ return $vsInstallDir;
+}
+
+sub msBuildInstallDir
+{
+ return $msBuildInstallDir if defined $msBuildInstallDir;
+
+ $msBuildInstallDir = File::Spec->catdir(programFilesPath(), "MSBuild", "14.0", "Bin");
+
+ chomp($msBuildInstallDir = `cygpath "$msBuildInstallDir"`) if isCygwin();
+
+ print "Using MSBuild: $msBuildInstallDir\n";
+ return $msBuildInstallDir;
+}
+
+sub visualStudioVersion
+{
+ return $vsVersion if defined $vsVersion;
+
+ my $installDir = visualStudioInstallDir();
+
+ $vsVersion = ($installDir =~ /Microsoft Visual Studio ([0-9]+\.[0-9]*)/) ? $1 : "14";
+
+ print "Using Visual Studio $vsVersion\n";
+ return $vsVersion;
+}
+
+sub determineConfigurationForVisualStudio
+{
+ return if defined $configurationForVisualStudio;
+ determineConfiguration();
+ # FIXME: We should detect when Debug_All or Production has been chosen.
+ $configurationForVisualStudio = "/p:Configuration=" . $configuration;
+}
+
+sub usesPerConfigurationBuildDirectory
+{
+ # [Gtk] We don't have Release/Debug configurations in straight
+ # autotool builds (non build-webkit). In this case and if
+ # WEBKIT_OUTPUTDIR exist, use that as our configuration dir. This will
+ # allows us to run run-webkit-tests without using build-webkit.
+ return ($ENV{"WEBKIT_OUTPUTDIR"} && isGtk()) || isAppleWinWebKit();
+}
+
+sub determineConfigurationProductDir
+{
+ return if defined $configurationProductDir;
+ determineBaseProductDir();
+ determineConfiguration();
+ if (isAppleWinWebKit() || isWinCairo()) {
+ my $binDir = isWin64() ? "bin64" : "bin32";
+ $configurationProductDir = File::Spec->catdir($baseProductDir, $configuration, $binDir);
+ } else {
+ if (usesPerConfigurationBuildDirectory()) {
+ $configurationProductDir = "$baseProductDir";
+ } else {
+ $configurationProductDir = "$baseProductDir/$configuration";
+ $configurationProductDir .= "-" . xcodeSDKPlatformName() if isIOSWebKit();
+ }
+ }
+}
+
+sub setConfigurationProductDir($)
+{
+ ($configurationProductDir) = @_;
+}
+
+sub determineCurrentSVNRevision
+{
+ # We always update the current SVN revision here, and leave the caching
+ # to currentSVNRevision(), so that changes to the SVN revision while the
+ # script is running can be picked up by calling this function again.
+ determineSourceDir();
+ $currentSVNRevision = svnRevisionForDirectory($sourceDir);
+ return $currentSVNRevision;
+}
+
+
+sub chdirWebKit
+{
+ determineSourceDir();
+ chdir $sourceDir or die;
+}
+
+sub baseProductDir
+{
+ determineBaseProductDir();
+ return $baseProductDir;
+}
+
+sub sourceDir
+{
+ determineSourceDir();
+ return $sourceDir;
+}
+
+sub productDir
+{
+ determineConfigurationProductDir();
+ return $configurationProductDir;
+}
+
+sub jscProductDir
+{
+ my $productDir = productDir();
+ $productDir .= "/bin" if (isEfl() || isGtk());
+
+ return $productDir;
+}
+
+sub configuration()
+{
+ determineConfiguration();
+ return $configuration;
+}
+
+sub asanIsEnabled()
+{
+ determineASanIsEnabled();
+ return $asanIsEnabled;
+}
+
+sub configurationForVisualStudio()
+{
+ determineConfigurationForVisualStudio();
+ return $configurationForVisualStudio;
+}
+
+sub currentSVNRevision
+{
+ determineCurrentSVNRevision() if not defined $currentSVNRevision;
+ return $currentSVNRevision;
+}
+
+sub generateDsym()
+{
+ determineGenerateDsym();
+ return $generateDsym;
+}
+
+sub determineGenerateDsym()
+{
+ return if defined($generateDsym);
+ $generateDsym = checkForArgumentAndRemoveFromARGV("--dsym");
+}
+
+sub argumentsForXcode()
+{
+ my @args = ();
+ push @args, "DEBUG_INFORMATION_FORMAT=dwarf-with-dsym" if generateDsym();
+ return @args;
+}
+
+sub XcodeOptions
+{
+ determineBaseProductDir();
+ determineConfiguration();
+ determineArchitecture();
+ determineASanIsEnabled();
+ determineXcodeSDK();
+
+ my @sdkOption = ($xcodeSDK ? "SDKROOT=$xcodeSDK" : ());
+ my @architectureOption = ($architecture ? "ARCHS=$architecture" : ());
+ my @asanOption = ($asanIsEnabled ? ("-xcconfig", sourceDir() . "/Tools/asan/asan.xcconfig", "ASAN_IGNORE=" . sourceDir() . "/Tools/asan/webkit-asan-ignore.txt") : ());
+
+ return ("-UseSanitizedBuildSystemEnvironment=YES", @baseProductDirOption, "-configuration", $configuration, @architectureOption, @sdkOption, @asanOption, argumentsForXcode());
+}
+
+sub XcodeOptionString
+{
+ return join " ", XcodeOptions();
+}
+
+sub XcodeOptionStringNoConfig
+{
+ return join " ", @baseProductDirOption;
+}
+
+sub XcodeCoverageSupportOptions()
+{
+ my @coverageSupportOptions = ();
+ push @coverageSupportOptions, "GCC_GENERATE_TEST_COVERAGE_FILES=YES";
+ push @coverageSupportOptions, "GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES";
+ return @coverageSupportOptions;
+}
+
+sub XcodeStaticAnalyzerOption()
+{
+ return "RUN_CLANG_STATIC_ANALYZER=YES";
+}
+
+my $passedConfiguration;
+my $searchedForPassedConfiguration;
+sub determinePassedConfiguration
+{
+ return if $searchedForPassedConfiguration;
+ $searchedForPassedConfiguration = 1;
+ $passedConfiguration = undef;
+
+ if (checkForArgumentAndRemoveFromARGV("--debug")) {
+ $passedConfiguration = "Debug";
+ } elsif(checkForArgumentAndRemoveFromARGV("--release")) {
+ $passedConfiguration = "Release";
+ } elsif (checkForArgumentAndRemoveFromARGV("--profile") || checkForArgumentAndRemoveFromARGV("--profiling")) {
+ $passedConfiguration = "Profiling";
+ }
+}
+
+sub passedConfiguration
+{
+ determinePassedConfiguration();
+ return $passedConfiguration;
+}
+
+sub setConfiguration
+{
+ setArchitecture();
+
+ if (my $config = shift @_) {
+ $configuration = $config;
+ return;
+ }
+
+ determinePassedConfiguration();
+ $configuration = $passedConfiguration if $passedConfiguration;
+}
+
+
+my $passedArchitecture;
+my $searchedForPassedArchitecture;
+sub determinePassedArchitecture
+{
+ return if $searchedForPassedArchitecture;
+ $searchedForPassedArchitecture = 1;
+
+ $passedArchitecture = undef;
+ if (checkForArgumentAndRemoveFromARGV("--32-bit")) {
+ if (isAppleMacWebKit()) {
+ # PLATFORM_IOS: Don't run `arch` command inside Simulator environment
+ local %ENV = %ENV;
+ delete $ENV{DYLD_ROOT_PATH};
+ delete $ENV{DYLD_FRAMEWORK_PATH};
+
+ $passedArchitecture = `arch`;
+ chomp $passedArchitecture;
+ }
+ }
+}
+
+sub passedArchitecture
+{
+ determinePassedArchitecture();
+ return $passedArchitecture;
+}
+
+sub architecture()
+{
+ determineArchitecture();
+ return $architecture;
+}
+
+sub numberOfCPUs()
+{
+ determineNumberOfCPUs();
+ return $numberOfCPUs;
+}
+
+sub maxCPULoad()
+{
+ determineMaxCPULoad();
+ return $maxCPULoad;
+}
+
+sub setArchitecture
+{
+ if (my $arch = shift @_) {
+ $architecture = $arch;
+ return;
+ }
+
+ determinePassedArchitecture();
+ $architecture = $passedArchitecture if $passedArchitecture;
+}
+
+sub skipSafariExecutableEntitlementChecks
+{
+ return `defaults read /Library/Preferences/org.webkit.BuildConfiguration SkipSafariExecutableEntitlementChecks 2>/dev/null` eq "1\n";
+}
+
+sub executableHasEntitlements
+{
+ my $executablePath = shift;
+ return (`codesign -d --entitlements - $executablePath 2>&1` =~ /<key>/);
+}
+
+sub safariPathFromSafariBundle
+{
+ my ($safariBundle) = @_;
+
+ die "Safari path is only relevant on Apple Mac platform\n" unless isAppleMacWebKit();
+
+ my $safariPath = "$safariBundle/Contents/MacOS/Safari";
+ return $safariPath if skipSafariExecutableEntitlementChecks();
+
+ my $safariForWebKitDevelopmentPath = "$safariBundle/Contents/MacOS/SafariForWebKitDevelopment";
+ return $safariForWebKitDevelopmentPath if -f $safariForWebKitDevelopmentPath && executableHasEntitlements($safariPath);
+
+ return $safariPath;
+}
+
+sub installedSafariPath
+{
+ return safariPathFromSafariBundle("/Applications/Safari.app");
+}
+
+# Locate Safari.
+sub safariPath
+{
+ die "Safari path is only relevant on Apple Mac platform\n" unless isAppleMacWebKit();
+
+ # Use WEBKIT_SAFARI environment variable if present.
+ my $safariBundle = $ENV{WEBKIT_SAFARI};
+ if (!$safariBundle) {
+ determineConfigurationProductDir();
+ # Use Safari.app in product directory if present (good for Safari development team).
+ if (-d "$configurationProductDir/Safari.app") {
+ $safariBundle = "$configurationProductDir/Safari.app";
+ }
+ if (!$safariBundle) {
+ return installedSafariPath();
+ }
+ }
+ my $safariPath = safariPathFromSafariBundle($safariBundle);
+ die "Can't find executable at $safariPath.\n" if !-x $safariPath;
+ return $safariPath;
+}
+
+sub builtDylibPathForName
+{
+ my $libraryName = shift;
+ determineConfigurationProductDir();
+
+ if (isGtk()) {
+ my $extension = isDarwin() ? ".dylib" : ".so";
+ return "$configurationProductDir/lib/libwebkit2gtk-4.0" . $extension;
+ }
+ if (isEfl()) {
+ return "$configurationProductDir/lib/libewebkit2.so";
+ }
+ if (isIOSWebKit()) {
+ return "$configurationProductDir/$libraryName.framework/$libraryName";
+ }
+ if (isAppleMacWebKit()) {
+ return "$configurationProductDir/$libraryName.framework/Versions/A/$libraryName";
+ }
+ if (isAppleWinWebKit()) {
+ if ($libraryName eq "JavaScriptCore") {
+ return "$baseProductDir/lib/$libraryName.lib";
+ } else {
+ return "$baseProductDir/$libraryName.intermediate/$configuration/$libraryName.intermediate/$libraryName.lib";
+ }
+ }
+
+ die "Unsupported platform, can't determine built library locations.\nTry `build-webkit --help` for more information.\n";
+}
+
+# Check to see that all the frameworks are built.
+sub checkFrameworks # FIXME: This is a poor name since only the Mac calls built WebCore a Framework.
+{
+ return if isCygwin() || isWindows();
+ my @frameworks = ("JavaScriptCore", "WebCore");
+ push(@frameworks, "WebKit") if isAppleMacWebKit(); # FIXME: This seems wrong, all ports should have a WebKit these days.
+ for my $framework (@frameworks) {
+ my $path = builtDylibPathForName($framework);
+ die "Can't find built framework at \"$path\".\n" unless -e $path;
+ }
+}
+
+sub isInspectorFrontend()
+{
+ determineIsInspectorFrontend();
+ return $isInspectorFrontend;
+}
+
+sub determineIsInspectorFrontend()
+{
+ return if defined($isInspectorFrontend);
+ $isInspectorFrontend = checkForArgumentAndRemoveFromARGV("--inspector-frontend");
+}
+
+sub commandExists($)
+{
+ my $command = shift;
+ my $devnull = File::Spec->devnull();
+
+ if (isAnyWindows()) {
+ return exitStatus(system("where /q $command >$devnull 2>&1")) == 0;
+ }
+ return exitStatus(system("which $command >$devnull 2>&1")) == 0;
+}
+
+sub checkForArgumentAndRemoveFromARGV($)
+{
+ my $argToCheck = shift;
+ return checkForArgumentAndRemoveFromArrayRef($argToCheck, \@ARGV);
+}
+
+sub checkForArgumentAndRemoveFromArrayRefGettingValue($$$)
+{
+ my ($argToCheck, $valueRef, $arrayRef) = @_;
+ my $argumentStartRegEx = qr#^$argToCheck(?:=\S|$)#;
+ my $i = 0;
+ for (; $i < @$arrayRef; ++$i) {
+ last if $arrayRef->[$i] =~ $argumentStartRegEx;
+ }
+ if ($i >= @$arrayRef) {
+ return $$valueRef = undef;
+ }
+ my ($key, $value) = split("=", $arrayRef->[$i]);
+ splice(@$arrayRef, $i, 1);
+ if (defined($value)) {
+ # e.g. --sdk=iphonesimulator
+ return $$valueRef = $value;
+ }
+ return $$valueRef = splice(@$arrayRef, $i, 1); # e.g. --sdk iphonesimulator
+}
+
+sub checkForArgumentAndRemoveFromARGVGettingValue($$)
+{
+ my ($argToCheck, $valueRef) = @_;
+ return checkForArgumentAndRemoveFromArrayRefGettingValue($argToCheck, $valueRef, \@ARGV);
+}
+
+sub findMatchingArguments($$)
+{
+ my ($argToCheck, $arrayRef) = @_;
+ my @matchingIndices;
+ foreach my $index (0 .. $#$arrayRef) {
+ my $opt = $$arrayRef[$index];
+ if ($opt =~ /^$argToCheck$/i ) {
+ push(@matchingIndices, $index);
+ }
+ }
+ return @matchingIndices;
+}
+
+sub hasArgument($$)
+{
+ my ($argToCheck, $arrayRef) = @_;
+ my @matchingIndices = findMatchingArguments($argToCheck, $arrayRef);
+ return scalar @matchingIndices > 0;
+}
+
+sub checkForArgumentAndRemoveFromArrayRef
+{
+ my ($argToCheck, $arrayRef) = @_;
+ my @indicesToRemove = findMatchingArguments($argToCheck, $arrayRef);
+ my $removeOffset = 0;
+ foreach my $index (@indicesToRemove) {
+ splice(@$arrayRef, $index - $removeOffset++, 1);
+ }
+ return scalar @indicesToRemove > 0;
+}
+
+sub determineIsEfl()
+{
+ return if defined($isEfl);
+ $isEfl = checkForArgumentAndRemoveFromARGV("--efl");
+}
+
+sub isEfl()
+{
+ determineIsEfl();
+ return $isEfl;
+}
+
+sub determineIsGtk()
+{
+ return if defined($isGtk);
+ $isGtk = checkForArgumentAndRemoveFromARGV("--gtk");
+}
+
+sub isGtk()
+{
+ determineIsGtk();
+ return $isGtk;
+}
+
+# Determine if this is debian, ubuntu, linspire, or something similar.
+sub isDebianBased()
+{
+ return -e "/etc/debian_version";
+}
+
+sub isFedoraBased()
+{
+ return -e "/etc/fedora-release";
+}
+
+sub isWinCairo()
+{
+ determineIsWinCairo();
+ return $isWinCairo;
+}
+
+sub determineIsWinCairo()
+{
+ return if defined($isWinCairo);
+ $isWinCairo = checkForArgumentAndRemoveFromARGV("--wincairo");
+}
+
+sub isWin64()
+{
+ determineIsWin64();
+ return $isWin64;
+}
+
+sub determineIsWin64()
+{
+ return if defined($isWin64);
+ $isWin64 = checkForArgumentAndRemoveFromARGV("--64-bit");
+}
+
+sub isCygwin()
+{
+ return ($^O eq "cygwin") || 0;
+}
+
+sub isAnyWindows()
+{
+ return isWindows() || isCygwin();
+}
+
+sub determineWinVersion()
+{
+ return if $winVersion;
+
+ if (!isAnyWindows()) {
+ $winVersion = -1;
+ return;
+ }
+
+ my $versionString = `cmd /c ver`;
+ $versionString =~ /(\d)\.(\d)\.(\d+)/;
+
+ $winVersion = {
+ major => $1,
+ minor => $2,
+ build => $3,
+ };
+}
+
+sub winVersion()
+{
+ determineWinVersion();
+ return $winVersion;
+}
+
+sub isWindows7SP0()
+{
+ return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 1 && winVersion()->{build} == 7600;
+}
+
+sub isWindowsVista()
+{
+ return isAnyWindows() && winVersion()->{major} == 6 && winVersion()->{minor} == 0;
+}
+
+sub isWindowsXP()
+{
+ return isAnyWindows() && winVersion()->{major} == 5 && winVersion()->{minor} == 1;
+}
+
+sub isDarwin()
+{
+ return ($^O eq "darwin") || 0;
+}
+
+sub isWindows()
+{
+ return ($^O eq "MSWin32") || 0;
+}
+
+sub isLinux()
+{
+ return ($^O eq "linux") || 0;
+}
+
+sub isBSD()
+{
+ return ($^O eq "freebsd") || ($^O eq "openbsd") || ($^O eq "netbsd") || 0;
+}
+
+sub isARM()
+{
+ return ($Config{archname} =~ /^arm[v\-]/) || ($Config{archname} =~ /^aarch64[v\-]/);
+}
+
+sub isCrossCompilation()
+{
+ my $compiler = "";
+ $compiler = $ENV{'CC'} if (defined($ENV{'CC'}));
+ if ($compiler =~ /gcc/) {
+ my $compiler_options = `$compiler -v 2>&1`;
+ my @host = $compiler_options =~ m/--host=(.*?)\s/;
+ my @target = $compiler_options =~ m/--target=(.*?)\s/;
+
+ return ($host[0] ne "" && $target[0] ne "" && $host[0] ne $target[0]);
+ }
+ return 0;
+}
+
+sub isAppleWebKit()
+{
+ return isAppleMacWebKit() || isAppleWinWebKit();
+}
+
+sub isAppleMacWebKit()
+{
+ return isDarwin() && !isGtk();
+}
+
+sub isAppleWinWebKit()
+{
+ return (isCygwin() || isWindows()) && !isWinCairo() && !isGtk();
+}
+
+sub iOSSimulatorDevicesPath
+{
+ return "$ENV{HOME}/Library/Developer/CoreSimulator/Devices";
+}
+
+sub iOSSimulatorDevices
+{
+ eval "require Foundation";
+ my $devicesPath = iOSSimulatorDevicesPath();
+ opendir(DEVICES, $devicesPath);
+ my @udids = grep {
+ $_ =~ m/[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}/;
+ } readdir(DEVICES);
+ close(DEVICES);
+
+ # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known
+ # dictionary keys so as to decouple our representation of the plist from the actual structure
+ # of the plist, which may change.
+ my @devices = map {
+ Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_("$devicesPath/$_/device.plist"));
+ } @udids;
+
+ return @devices;
+}
+
+sub createiOSSimulatorDevice
+{
+ my $name = shift;
+ my $deviceTypeId = shift;
+ my $runtimeId = shift;
+
+ my $created = system("xcrun", "--sdk", "iphonesimulator", "simctl", "create", $name, $deviceTypeId, $runtimeId) == 0;
+ die "Couldn't create simulator device: $name $deviceTypeId $runtimeId" if not $created;
+
+ system("xcrun", "--sdk", "iphonesimulator", "simctl", "list");
+
+ print "Waiting for device to be created ...\n";
+ sleep 5;
+ for (my $tries = 0; $tries < 5; $tries++){
+ my @devices = iOSSimulatorDevices();
+ foreach my $device (@devices) {
+ return $device if $device->{name} eq $name and $device->{deviceType} eq $deviceTypeId and $device->{runtime} eq $runtimeId;
+ }
+ sleep 5;
+ }
+ die "Device $name $deviceTypeId $runtimeId wasn't found in " . iOSSimulatorDevicesPath();
+}
+
+sub willUseIOSDeviceSDKWhenBuilding()
+{
+ return xcodeSDKPlatformName() eq "iphoneos";
+}
+
+sub willUseIOSSimulatorSDKWhenBuilding()
+{
+ return xcodeSDKPlatformName() eq "iphonesimulator";
+}
+
+sub isIOSWebKit()
+{
+ determineXcodeSDK();
+ return isAppleMacWebKit() && (willUseIOSDeviceSDKWhenBuilding() || willUseIOSSimulatorSDKWhenBuilding());
+}
+
+sub determineNmPath()
+{
+ return if $nmPath;
+
+ if (isAppleMacWebKit()) {
+ $nmPath = `xcrun -find nm`;
+ chomp $nmPath;
+ }
+ $nmPath = "nm" if !$nmPath;
+}
+
+sub nmPath()
+{
+ determineNmPath();
+ return $nmPath;
+}
+
+sub determineOSXVersion()
+{
+ return if $osXVersion;
+
+ if (!isDarwin()) {
+ $osXVersion = -1;
+ return;
+ }
+
+ my $version = `sw_vers -productVersion`;
+ my @splitVersion = split(/\./, $version);
+ @splitVersion >= 2 or die "Invalid version $version";
+ $osXVersion = {
+ "major" => $splitVersion[0],
+ "minor" => $splitVersion[1],
+ "subminor" => (defined($splitVersion[2]) ? $splitVersion[2] : 0),
+ };
+}
+
+sub osXVersion()
+{
+ determineOSXVersion();
+ return $osXVersion;
+}
+
+sub isWindowsNT()
+{
+ return $ENV{'OS'} eq 'Windows_NT';
+}
+
+sub shouldTargetWebProcess
+{
+ determineShouldTargetWebProcess();
+ return $shouldTargetWebProcess;
+}
+
+sub determineShouldTargetWebProcess
+{
+ return if defined($shouldTargetWebProcess);
+ $shouldTargetWebProcess = checkForArgumentAndRemoveFromARGV("--target-web-process");
+}
+
+sub shouldUseXPCServiceForWebProcess
+{
+ determineShouldUseXPCServiceForWebProcess();
+ return $shouldUseXPCServiceForWebProcess;
+}
+
+sub determineShouldUseXPCServiceForWebProcess
+{
+ return if defined($shouldUseXPCServiceForWebProcess);
+ $shouldUseXPCServiceForWebProcess = checkForArgumentAndRemoveFromARGV("--use-web-process-xpc-service");
+}
+
+sub debugger
+{
+ determineDebugger();
+ return $debugger;
+}
+
+sub determineDebugger
+{
+ return if defined($debugger);
+
+ determineXcodeVersion();
+ if (eval "v$xcodeVersion" ge v4.5) {
+ $debugger = "lldb";
+ } else {
+ $debugger = "gdb";
+ }
+
+ if (checkForArgumentAndRemoveFromARGV("--use-lldb")) {
+ $debugger = "lldb";
+ }
+
+ if (checkForArgumentAndRemoveFromARGV("--use-gdb")) {
+ $debugger = "gdb";
+ }
+}
+
+sub appendToEnvironmentVariableList
+{
+ my ($environmentVariableName, $value) = @_;
+
+ if (defined($ENV{$environmentVariableName})) {
+ $ENV{$environmentVariableName} .= ":" . $value;
+ } else {
+ $ENV{$environmentVariableName} = $value;
+ }
+}
+
+sub sharedCommandLineOptions()
+{
+ return (
+ "g|guard-malloc" => \$shouldUseGuardMalloc,
+ );
+}
+
+sub sharedCommandLineOptionsUsage
+{
+ my %opts = @_;
+
+ my %switches = (
+ '-g|--guard-malloc' => 'Use guardmalloc when running executable',
+ );
+
+ my $indent = " " x ($opts{indent} || 2);
+ my $switchWidth = List::Util::max(int($opts{switchWidth}), List::Util::max(map { length($_) } keys %switches) + ($opts{brackets} ? 2 : 0));
+
+ my $result = "Common switches:\n";
+
+ for my $switch (keys %switches) {
+ my $switchName = $opts{brackets} ? "[" . $switch . "]" : $switch;
+ $result .= sprintf("%s%-" . $switchWidth . "s %s\n", $indent, $switchName, $switches{$switch});
+ }
+
+ return $result;
+}
+
+sub setUpGuardMallocIfNeeded
+{
+ if (!isDarwin()) {
+ return;
+ }
+
+ if (!defined($shouldUseGuardMalloc)) {
+ $shouldUseGuardMalloc = checkForArgumentAndRemoveFromARGV("-g") || checkForArgumentAndRemoveFromARGV("--guard-malloc");
+ }
+
+ if ($shouldUseGuardMalloc) {
+ appendToEnvironmentVariableList("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib");
+ }
+}
+
+sub relativeScriptsDir()
+{
+ my $scriptDir = File::Spec->catpath("", File::Spec->abs2rel($FindBin::Bin, getcwd()), "");
+ if ($scriptDir eq "") {
+ $scriptDir = ".";
+ }
+ return $scriptDir;
+}
+
+sub launcherPath()
+{
+ my $relativeScriptsPath = relativeScriptsDir();
+ if (isGtk() || isEfl()) {
+ return "$relativeScriptsPath/run-minibrowser";
+ } elsif (isAppleWebKit()) {
+ return "$relativeScriptsPath/run-safari";
+ }
+}
+
+sub launcherName()
+{
+ if (isGtk() || isEfl()) {
+ return "MiniBrowser";
+ } elsif (isAppleMacWebKit()) {
+ return "Safari";
+ } elsif (isAppleWinWebKit()) {
+ return "WinLauncher";
+ }
+}
+
+sub checkRequiredSystemConfig
+{
+ if (isDarwin()) {
+ chomp(my $productVersion = `sw_vers -productVersion`);
+ if (eval "v$productVersion" lt v10.7.5) {
+ print "*************************************************************\n";
+ print "Mac OS X Version 10.7.5 or later is required to build WebKit.\n";
+ print "You have " . $productVersion . ", thus the build will most likely fail.\n";
+ print "*************************************************************\n";
+ }
+ my $xcodebuildVersionOutput = `xcodebuild -version`;
+ my $xcodeVersion = ($xcodebuildVersionOutput =~ /Xcode ([0-9](\.[0-9]+)*)/) ? $1 : undef;
+ if (!$xcodeVersion || $xcodeVersion && eval "v$xcodeVersion" lt v4.6) {
+ print "*************************************************************\n";
+ print "Xcode Version 4.6 or later is required to build WebKit.\n";
+ print "You have an earlier version of Xcode, thus the build will\n";
+ print "most likely fail. The latest Xcode is available from the App Store.\n";
+ print "*************************************************************\n";
+ }
+ } elsif (isGtk() or isEfl() or isWindows()) {
+ my @cmds = qw(bison gperf flex);
+ my @missing = ();
+ my $oldPath = $ENV{PATH};
+ foreach my $cmd (@cmds) {
+ push @missing, $cmd if not commandExists($cmd);
+ }
+
+ if (@missing) {
+ my $list = join ", ", @missing;
+ die "ERROR: $list missing but required to build WebKit.\n";
+ }
+ }
+ # Win32 and other platforms may want to check for minimum config
+}
+
+sub determineWindowsSourceDir()
+{
+ return if $windowsSourceDir;
+ $windowsSourceDir = sourceDir();
+ chomp($windowsSourceDir = `cygpath -w '$windowsSourceDir'`) if isCygwin();
+}
+
+sub windowsSourceDir()
+{
+ determineWindowsSourceDir();
+ return $windowsSourceDir;
+}
+
+sub windowsSourceSourceDir()
+{
+ return File::Spec->catdir(windowsSourceDir(), "Source");
+}
+
+sub windowsLibrariesDir()
+{
+ return File::Spec->catdir(windowsSourceDir(), "WebKitLibraries", "win");
+}
+
+sub windowsOutputDir()
+{
+ return File::Spec->catdir(windowsSourceDir(), "WebKitBuild");
+}
+
+sub fontExists($)
+{
+ my $font = shift;
+ my $cmd = "reg query \"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\" . $font ."\" 2>&1";
+ my $val = `$cmd`;
+ return $? == 0;
+}
+
+sub checkInstalledTools()
+{
+ # environment variables. Avoid until this is corrected.
+ my $pythonVer = `python --version 2>&1`;
+ die "You must have Python installed to build WebKit.\n" if ($?);
+
+ # cURL 7.34.0 has a bug that prevents authentication with opensource.apple.com (and other things using SSL3).
+ my $curlVer = `curl --version 2> NUL`;
+ if (!$? and $curlVer =~ "(.*curl.*)") {
+ $curlVer = $1;
+ if ($curlVer =~ /libcurl\/7\.34\.0/) {
+ print "cURL version 7.34.0 has a bug that prevents authentication with SSL v2 or v3.\n";
+ print "cURL 7.33.0 is known to work. The cURL projects is preparing an update to\n";
+ print "correct this problem.\n\n";
+ die "Please install a working cURL and try again.\n";
+ }
+ }
+
+ # MathML requires fonts that do not ship with Windows (at least through Windows 8). Warn the user if they are missing
+ my @fonts = qw(STIXGeneral-Regular MathJax_Main-Regular);
+ my @missing = ();
+ foreach my $font (@fonts) {
+ push @missing, $font if not fontExists($font);
+ }
+
+ if (scalar @missing > 0) {
+ print "*************************************************************\n";
+ print "Mathematical fonts, such as STIX and MathJax, are needed to\n";
+ print "use the MathML feature. You do not appear to have these fonts\n";
+ print "on your system.\n\n";
+ print "You can download a suitable set of fonts from the following URL:\n";
+ print "https://developer.mozilla.org/Mozilla/MathML_Projects/Fonts\n";
+ print "*************************************************************\n";
+ }
+
+ print "Installed tools are correct for the WebKit build.\n";
+}
+
+sub setupAppleWinEnv()
+{
+ return unless isAppleWinWebKit();
+
+ checkInstalledTools();
+
+ if (isWindowsNT()) {
+ my $restartNeeded = 0;
+ my %variablesToSet = ();
+
+ # FIXME: We should remove this explicit version check for cygwin once we stop supporting Cygwin 1.7.9 or older versions.
+ # https://bugs.webkit.org/show_bug.cgi?id=85791
+ my $uname_version = (POSIX::uname())[2];
+ $uname_version =~ s/\(.*\)//; # Remove the trailing cygwin version, if any.
+ $uname_version =~ s/\-.*$//; # Remove trailing dash-version content, if any
+ if (version->parse($uname_version) < version->parse("1.7.10")) {
+ # Setting the environment variable 'CYGWIN' to 'tty' makes cygwin enable extra support (i.e., termios)
+ # for UNIX-like ttys in the Windows console
+ $variablesToSet{CYGWIN} = "tty" unless $ENV{CYGWIN};
+ }
+
+ # Those environment variables must be set to be able to build inside Visual Studio.
+ $variablesToSet{WEBKIT_LIBRARIES} = windowsLibrariesDir() unless $ENV{WEBKIT_LIBRARIES};
+ $variablesToSet{WEBKIT_OUTPUTDIR} = windowsOutputDir() unless $ENV{WEBKIT_OUTPUTDIR};
+ $variablesToSet{MSBUILDDISABLENODEREUSE} = "1" unless $ENV{MSBUILDDISABLENODEREUSE};
+
+ foreach my $variable (keys %variablesToSet) {
+ print "Setting the Environment Variable '" . $variable . "' to '" . $variablesToSet{$variable} . "'\n\n";
+ my $ret = system "setx", $variable, $variablesToSet{$variable};
+ if ($ret != 0) {
+ system qw(regtool -s set), '\\HKEY_CURRENT_USER\\Environment\\' . $variable, $variablesToSet{$variable};
+ }
+ $restartNeeded ||= $variable eq "WEBKIT_LIBRARIES" || $variable eq "WEBKIT_OUTPUTDIR";
+ }
+
+ if ($restartNeeded) {
+ print "Please restart your computer before attempting to build inside Visual Studio.\n\n";
+ }
+ } else {
+ if (!defined $ENV{'WEBKIT_LIBRARIES'} || !$ENV{'WEBKIT_LIBRARIES'}) {
+ print "Warning: You must set the 'WebKit_Libraries' environment variable\n";
+ print " to be able build WebKit from within Visual Studio 2013 and newer.\n";
+ print " Make sure that 'WebKit_Libraries' points to the\n";
+ print " 'WebKitLibraries/win' directory, not the 'WebKitLibraries/' directory.\n\n";
+ }
+ if (!defined $ENV{'WEBKIT_OUTPUTDIR'} || !$ENV{'WEBKIT_OUTPUTDIR'}) {
+ print "Warning: You must set the 'WebKit_OutputDir' environment variable\n";
+ print " to be able build WebKit from within Visual Studio 2013 and newer.\n\n";
+ }
+ if (!defined $ENV{'MSBUILDDISABLENODEREUSE'} || !$ENV{'MSBUILDDISABLENODEREUSE'}) {
+ print "Warning: You should set the 'MSBUILDDISABLENODEREUSE' environment variable to '1'\n";
+ print " to avoid periodic locked log files when building.\n\n";
+ }
+ }
+ # FIXME (125180): Remove the following temporary 64-bit support once official support is available.
+ if (isWin64() and !$ENV{'WEBKIT_64_SUPPORT'}) {
+ print "Warning: You must set the 'WEBKIT_64_SUPPORT' environment variable\n";
+ print " to be able run WebKit or JavaScriptCore tests.\n\n";
+ }
+}
+
+sub setupCygwinEnv()
+{
+ return if !isCygwin() && !isWindows();
+ return if $vcBuildPath;
+
+ my $programFilesPath = programFilesPath();
+ my $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.com));
+ if (-e $visualStudioPath) {
+ # Visual Studio is installed;
+ if (visualStudioVersion() eq "12") {
+ $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE devenv.exe));
+ }
+ } else {
+ # Visual Studio not found, try VC++ Express
+ $visualStudioPath = File::Spec->catfile(visualStudioInstallDir(), qw(Common7 IDE WDExpress.exe));
+ if (! -e $visualStudioPath) {
+ print "*************************************************************\n";
+ print "Cannot find '$visualStudioPath'\n";
+ print "Please execute the file 'vcvars32.bat' from\n";
+ print "'$programFilesPath\\Microsoft Visual Studio 14.0\\VC\\bin\\'\n";
+ print "to setup the necessary environment variables.\n";
+ print "*************************************************************\n";
+ die;
+ }
+ $willUseVCExpressWhenBuilding = 1;
+ }
+
+ print "Building results into: ", baseProductDir(), "\n";
+ print "WEBKIT_OUTPUTDIR is set to: ", $ENV{"WEBKIT_OUTPUTDIR"}, "\n";
+ print "WEBKIT_LIBRARIES is set to: ", $ENV{"WEBKIT_LIBRARIES"}, "\n";
+ # FIXME (125180): Remove the following temporary 64-bit support once official support is available.
+ print "WEBKIT_64_SUPPORT is set to: ", $ENV{"WEBKIT_64_SUPPORT"}, "\n" if isWin64();
+
+ # We will actually use MSBuild to build WebKit, but we need to find the Visual Studio install (above) to make
+ # sure we use the right options.
+ $vcBuildPath = File::Spec->catfile(msBuildInstallDir(), qw(MSBuild.exe));
+ if (! -e $vcBuildPath) {
+ print "*************************************************************\n";
+ print "Cannot find '$vcBuildPath'\n";
+ print "Please make sure execute that the Microsoft .NET Framework SDK\n";
+ print "is installed on this machine.\n";
+ print "*************************************************************\n";
+ die;
+ }
+}
+
+sub dieIfWindowsPlatformSDKNotInstalled
+{
+ my $registry32Path = "/proc/registry/";
+ my $registry64Path = "/proc/registry64/";
+ my @windowsPlatformSDKRegistryEntries = (
+ "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0A",
+ "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v8.0",
+ "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.1A",
+ "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Microsoft SDKs/Windows/v7.0A",
+ "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MicrosoftSDK/InstalledSDKs/D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1",
+ );
+
+ # FIXME: It would be better to detect whether we are using 32- or 64-bit Windows
+ # and only check the appropriate entry. But for now we just blindly check both.
+ my $recommendedPlatformSDK = $windowsPlatformSDKRegistryEntries[0];
+
+ while (@windowsPlatformSDKRegistryEntries) {
+ my $windowsPlatformSDKRegistryEntry = shift @windowsPlatformSDKRegistryEntries;
+ return if (-e $registry32Path . $windowsPlatformSDKRegistryEntry) || (-e $registry64Path . $windowsPlatformSDKRegistryEntry);
+ }
+
+ print "*************************************************************\n";
+ print "Cannot find registry entry '$recommendedPlatformSDK'.\n";
+ print "Please download and install the Microsoft Windows SDK\n";
+ print "from <http://www.microsoft.com/en-us/download/details.aspx?id=8279>.\n\n";
+ print "Then follow step 2 in the Windows section of the \"Installing Developer\n";
+ print "Tools\" instructions at <http://www.webkit.org/building/tools.html>.\n";
+ print "*************************************************************\n";
+ die;
+}
+
+sub buildXCodeProject($$@)
+{
+ my ($project, $clean, @extraOptions) = @_;
+
+ if ($clean) {
+ push(@extraOptions, "-alltargets");
+ push(@extraOptions, "clean");
+ }
+
+ push(@extraOptions, ("-sdk", xcodeSDK())) if isIOSWebKit();
+
+ chomp($ENV{DSYMUTIL_NUM_THREADS} = `sysctl -n hw.activecpu`);
+ return system "xcodebuild", "-project", "$project.xcodeproj", @extraOptions;
+}
+
+sub usingVisualStudioExpress()
+{
+ setupCygwinEnv();
+ return $willUseVCExpressWhenBuilding;
+}
+
+sub buildVisualStudioProject
+{
+ my ($project, $clean) = @_;
+ setupCygwinEnv();
+
+ my $config = configurationForVisualStudio();
+
+ dieIfWindowsPlatformSDKNotInstalled() if $willUseVCExpressWhenBuilding;
+
+ chomp($project = `cygpath -w "$project"`) if isCygwin();
+
+ my $action = "/t:build";
+ if ($clean) {
+ $action = "/t:clean";
+ }
+
+ my $platform = "/p:Platform=" . (isWin64() ? "x64" : "Win32");
+ my $logPath = File::Spec->catdir($baseProductDir, $configuration);
+ make_path($logPath) unless -d $logPath or $logPath eq ".";
+
+ my $errorLogFile = File::Spec->catfile($logPath, "webkit_errors.log");
+ chomp($errorLogFile = `cygpath -w "$errorLogFile"`) if isCygwin();
+ my $errorLogging = "/flp:LogFile=" . $errorLogFile . ";ErrorsOnly";
+
+ my $warningLogFile = File::Spec->catfile($logPath, "webkit_warnings.log");
+ chomp($warningLogFile = `cygpath -w "$warningLogFile"`) if isCygwin();
+ my $warningLogging = "/flp1:LogFile=" . $warningLogFile . ";WarningsOnly";
+
+ my @command = ($vcBuildPath, "/verbosity:minimal", $project, $action, $config, $platform, "/fl", $errorLogging, "/fl1", $warningLogging);
+
+ print join(" ", @command), "\n";
+ return system @command;
+}
+
+sub getJhbuildPath()
+{
+ my @jhbuildPath = File::Spec->splitdir(baseProductDir());
+ if (isGit() && isGitBranchBuild() && gitBranch()) {
+ pop(@jhbuildPath);
+ }
+ if (isEfl()) {
+ push(@jhbuildPath, "DependenciesEFL");
+ } elsif (isGtk()) {
+ push(@jhbuildPath, "DependenciesGTK");
+ } else {
+ die "Cannot get JHBuild path for platform that isn't GTK+ or EFL.\n";
+ }
+ return File::Spec->catdir(@jhbuildPath);
+}
+
+sub isCachedArgumentfileOutOfDate($@)
+{
+ my ($filename, $currentContents) = @_;
+
+ if (! -e $filename) {
+ return 1;
+ }
+
+ open(CONTENTS_FILE, $filename);
+ chomp(my $previousContents = <CONTENTS_FILE>);
+ close(CONTENTS_FILE);
+
+ if ($previousContents ne $currentContents) {
+ print "Contents for file $filename have changed.\n";
+ print "Previous contents were: $previousContents\n\n";
+ print "New contents are: $currentContents\n";
+ return 1;
+ }
+
+ return 0;
+}
+
+sub jhbuildWrapperPrefixIfNeeded()
+{
+ if (isWindows()) {
+ return ();
+ }
+ if (-e getJhbuildPath()) {
+ my @prefix = (File::Spec->catfile(sourceDir(), "Tools", "jhbuild", "jhbuild-wrapper"));
+ if (isEfl()) {
+ push(@prefix, "--efl");
+ } elsif (isGtk()) {
+ push(@prefix, "--gtk");
+ }
+ push(@prefix, "run");
+
+ return @prefix;
+ }
+
+ return ();
+}
+
+sub cmakeCachePath()
+{
+ return File::Spec->catdir(baseProductDir(), configuration(), "CMakeCache.txt");
+}
+
+sub shouldRemoveCMakeCache(@)
+{
+ my ($cacheFilePath, @buildArgs) = @_;
+
+ # We check this first, because we always want to create this file for a fresh build.
+ my $productDir = File::Spec->catdir(baseProductDir(), configuration());
+ my $optionsCache = File::Spec->catdir($productDir, "build-webkit-options.txt");
+ my $joinedBuildArgs = join(" ", @buildArgs);
+ if (isCachedArgumentfileOutOfDate($optionsCache, $joinedBuildArgs)) {
+ File::Path::mkpath($productDir) unless -d $productDir;
+ open(CACHED_ARGUMENTS, ">", $optionsCache);
+ print CACHED_ARGUMENTS $joinedBuildArgs;
+ close(CACHED_ARGUMENTS);
+
+ return 1;
+ }
+
+ my $cmakeCache = cmakeCachePath();
+ unless (-e $cmakeCache) {
+ return 0;
+ }
+
+ my $cacheFileModifiedTime = stat($cmakeCache)->mtime;
+ my $platformConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "Options" . cmakeBasedPortName() . ".cmake");
+ if ($cacheFileModifiedTime < stat($platformConfiguration)->mtime) {
+ return 1;
+ }
+
+ my $globalConfiguration = File::Spec->catdir(sourceDir(), "Source", "cmake", "OptionsCommon.cmake");
+ if ($cacheFileModifiedTime < stat($globalConfiguration)->mtime) {
+ return 1;
+ }
+
+ my $inspectorUserInterfaceDircetory = File::Spec->catdir(sourceDir(), "Source", "WebInspectorUI", "UserInterface");
+ if ($cacheFileModifiedTime < stat($inspectorUserInterfaceDircetory)->mtime) {
+ return 1;
+ }
+
+ return 0;
+}
+
+sub removeCMakeCache(@)
+{
+ my (@buildArgs) = @_;
+ if (shouldRemoveCMakeCache(@buildArgs)) {
+ my $cmakeCache = cmakeCachePath();
+ unlink($cmakeCache) if -e $cmakeCache;
+ }
+}
+
+sub canUseNinja(@)
+{
+ # Test both ninja and ninja-build. Fedora uses ninja-build and has patched CMake to also call ninja-build.
+ return commandExists("ninja") || commandExists("ninja-build");
+}
+
+sub canUseEclipse(@)
+{
+ return commandExists("eclipse");
+}
+
+sub cmakeGeneratedBuildfile(@)
+{
+ my ($willUseNinja) = @_;
+ if ($willUseNinja) {
+ return File::Spec->catfile(baseProductDir(), configuration(), "build.ninja")
+ } else {
+ return File::Spec->catfile(baseProductDir(), configuration(), "Makefile")
+ }
+}
+
+sub generateBuildSystemFromCMakeProject
+{
+ my ($port, $prefixPath, @cmakeArgs, $additionalCMakeArgs) = @_;
+ my $config = configuration();
+ my $buildPath = File::Spec->catdir(baseProductDir(), $config);
+ File::Path::mkpath($buildPath) unless -d $buildPath;
+ my $originalWorkingDirectory = getcwd();
+ chdir($buildPath) or die;
+
+ # We try to be smart about when to rerun cmake, so that we can have faster incremental builds.
+ my $willUseNinja = canUseNinja();
+ if (-e cmakeCachePath() && -e cmakeGeneratedBuildfile($willUseNinja)) {
+ return 0;
+ }
+
+ my @args;
+ push @args, "-DPORT=\"$port\"";
+ push @args, "-DCMAKE_INSTALL_PREFIX=\"$prefixPath\"" if $prefixPath;
+ push @args, "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON" if isGtk();
+ if ($config =~ /release/i) {
+ push @args, "-DCMAKE_BUILD_TYPE=Release";
+ } elsif ($config =~ /debug/i) {
+ push @args, "-DCMAKE_BUILD_TYPE=Debug";
+ }
+
+ if ($willUseNinja) {
+ push @args, "-G";
+ if (canUseEclipse()) {
+ push @args, "'Eclipse CDT4 - Ninja'";
+ } else {
+ push @args, "Ninja";
+ }
+ }
+
+ # GTK+ has a production mode, but build-webkit should always use developer mode.
+ push @args, "-DDEVELOPER_MODE=ON" if isEfl() || isGtk();
+
+ # Don't warn variables which aren't used by cmake ports.
+ push @args, "--no-warn-unused-cli";
+ push @args, @cmakeArgs if @cmakeArgs;
+ push @args, $additionalCMakeArgs if $additionalCMakeArgs;
+
+ push @args, '"' . sourceDir() . '"';
+
+ # Compiler options to keep floating point values consistent
+ # between 32-bit and 64-bit architectures.
+ determineArchitecture();
+ if ($architecture ne "x86_64" && !isARM() && !isCrossCompilation() && !isWindows()) {
+ $ENV{'CXXFLAGS'} = "-march=pentium4 -msse2 -mfpmath=sse " . ($ENV{'CXXFLAGS'} || "");
+ }
+
+ # We call system("cmake @args") instead of system("cmake", @args) so that @args is
+ # parsed for shell metacharacters.
+ my $wrapper = join(" ", jhbuildWrapperPrefixIfNeeded()) . " ";
+ my $returnCode = system($wrapper . "cmake @args");
+
+ chdir($originalWorkingDirectory);
+ return $returnCode;
+}
+
+sub buildCMakeGeneratedProject($)
+{
+ my ($makeArgs) = @_;
+ my $config = configuration();
+ my $buildPath = File::Spec->catdir(baseProductDir(), $config);
+ if (! -d $buildPath) {
+ die "Must call generateBuildSystemFromCMakeProject() before building CMake project.";
+ }
+
+ my $command = "cmake";
+ my @args = ("--build", $buildPath, "--config", $config);
+ push @args, ("--", $makeArgs) if $makeArgs;
+
+ # GTK can use a build script to preserve colors and pretty-printing.
+ if (isGtk() && -e "$buildPath/build.sh") {
+ chdir "$buildPath" or die;
+ $command = "$buildPath/build.sh";
+ @args = ($makeArgs);
+ }
+
+ if ($ENV{VERBOSE} && canUseNinja()) {
+ push @args, "-v";
+ push @args, "-d keeprsp" if (version->parse(determineNinjaVersion()) >= version->parse("1.4.0"));
+ }
+
+ # We call system("cmake @args") instead of system("cmake", @args) so that @args is
+ # parsed for shell metacharacters. In particular, $makeArgs may contain such metacharacters.
+ my $wrapper = join(" ", jhbuildWrapperPrefixIfNeeded()) . " ";
+ return system($wrapper . "$command @args");
+
+}
+
+sub cleanCMakeGeneratedProject()
+{
+ my $config = configuration();
+ my $buildPath = File::Spec->catdir(baseProductDir(), $config);
+ if (-d $buildPath) {
+ return system("cmake", "--build", $buildPath, "--config", $config, "--target", "clean");
+ }
+ return 0;
+}
+
+sub buildCMakeProjectOrExit($$$$@)
+{
+ my ($clean, $port, $prefixPath, $makeArgs, @cmakeArgs) = @_;
+ my $returnCode;
+
+ exit(exitStatus(cleanCMakeGeneratedProject())) if $clean;
+
+ if (isEfl() && checkForArgumentAndRemoveFromARGV("--update-efl")) {
+ system("perl", "$sourceDir/Tools/Scripts/update-webkitefl-libs") == 0 or die $!;
+ }
+
+ if (isGtk() && checkForArgumentAndRemoveFromARGV("--update-gtk")) {
+ system("perl", "$sourceDir/Tools/Scripts/update-webkitgtk-libs") == 0 or die $!;
+ }
+
+ $returnCode = exitStatus(generateBuildSystemFromCMakeProject($port, $prefixPath, @cmakeArgs));
+ exit($returnCode) if $returnCode;
+
+ $returnCode = exitStatus(buildCMakeGeneratedProject($makeArgs));
+ exit($returnCode) if $returnCode;
+ return 0;
+}
+
+sub cmakeBasedPortArguments()
+{
+ return ();
+}
+
+sub cmakeBasedPortName()
+{
+ return "Efl" if isEfl();
+ return "GTK" if isGtk();
+ return "";
+}
+
+sub isCMakeBuild()
+{
+ return isEfl() || isGtk();
+}
+
+sub promptUser
+{
+ my ($prompt, $default) = @_;
+ my $defaultValue = $default ? "[$default]" : "";
+ print "$prompt $defaultValue: ";
+ chomp(my $input = <STDIN>);
+ return $input ? $input : $default;
+}
+
+sub appleApplicationSupportPath
+{
+ open INSTALL_DIR, "</proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/Apple\ Inc./Apple\ Application\ Support/InstallDir";
+ my $path = <INSTALL_DIR>;
+ $path =~ s/[\r\n\x00].*//;
+ close INSTALL_DIR;
+
+ my $unixPath = `cygpath -u '$path'`;
+ chomp $unixPath;
+ return $unixPath;
+}
+
+sub setPathForRunningWebKitApp
+{
+ my ($env) = @_;
+
+ if (isAppleWinWebKit()) {
+ $env->{PATH} = join(':', productDir(), appleApplicationSupportPath(), $env->{PATH} || "");
+ } elsif (isWinCairo()) {
+ my $winCairoBin = sourceDir() . "/WebKitLibraries/win/" . (isWin64() ? "bin64/" : "bin32/");
+ my $gstreamerBin = isWin64() ? $ENV{"GSTREAMER_1_0_ROOT_X86_64"} . "bin" : $ENV{"GSTREAMER_1_0_ROOT_X86"} . "bin";
+ $env->{PATH} = join(':', productDir(), $winCairoBin, $gstreamerBin, $env->{PATH} || "");
+ }
+}
+
+sub printHelpAndExitForRunAndDebugWebKitAppIfNeeded
+{
+ return unless checkForArgumentAndRemoveFromARGV("--help");
+
+ my ($includeOptionsForDebugging) = @_;
+
+ print STDERR <<EOF;
+Usage: @{[basename($0)]} [options] [args ...]
+ --help Show this help message
+ --no-saved-state Launch the application without state restoration (OS X 10.7 and later)
+ -g|--guard-malloc Enable Guard Malloc (OS X only)
+ --use-web-process-xpc-service Launch the Web Process as an XPC Service (OS X only)
+EOF
+
+ if ($includeOptionsForDebugging) {
+ print STDERR <<EOF;
+ --target-web-process Debug the web process
+ --use-gdb Use GDB (this is the default when using Xcode 4.4 or earlier)
+ --use-lldb Use LLDB (this is the default when using Xcode 4.5 or later)
+EOF
+ }
+
+ exit(1);
+}
+
+sub argumentsForRunAndDebugMacWebKitApp()
+{
+ my @args = ();
+ if (checkForArgumentAndRemoveFromARGV("--no-saved-state")) {
+ push @args, ("-ApplePersistenceIgnoreStateQuietly", "YES");
+ # FIXME: Don't set ApplePersistenceIgnoreState once all supported OS versions respect ApplePersistenceIgnoreStateQuietly (rdar://15032886).
+ push @args, ("-ApplePersistenceIgnoreState", "YES");
+ }
+ push @args, ("-WebKit2UseXPCServiceForWebProcess", "YES") if shouldUseXPCServiceForWebProcess();
+ unshift @args, @ARGV;
+
+ return @args;
+}
+
+sub setupMacWebKitEnvironment($)
+{
+ my ($dyldFrameworkPath) = @_;
+
+ $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath);
+
+ $ENV{DYLD_FRAMEWORK_PATH} = $ENV{DYLD_FRAMEWORK_PATH} ? join(":", $dyldFrameworkPath, $ENV{DYLD_FRAMEWORK_PATH}) : $dyldFrameworkPath;
+ $ENV{__XPC_DYLD_FRAMEWORK_PATH} = $dyldFrameworkPath;
+ $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES";
+
+ setUpGuardMallocIfNeeded();
+}
+
+sub setupIOSWebKitEnvironment($)
+{
+ my ($dyldFrameworkPath) = @_;
+ $dyldFrameworkPath = File::Spec->rel2abs($dyldFrameworkPath);
+
+ $ENV{DYLD_FRAMEWORK_PATH} = $dyldFrameworkPath;
+ $ENV{DYLD_LIBRARY_PATH} = $dyldFrameworkPath;
+
+ setUpGuardMallocIfNeeded();
+}
+
+sub iosSimulatorApplicationsPath()
+{
+ return File::Spec->catdir(XcodeSDKPath(), "Applications");
+}
+
+sub installedMobileSafariBundle()
+{
+ return File::Spec->catfile(iosSimulatorApplicationsPath(), "MobileSafari.app");
+}
+
+sub mobileSafariBundle()
+{
+ determineConfigurationProductDir();
+
+ # Use MobileSafari.app in product directory if present.
+ if (isAppleMacWebKit() && -d "$configurationProductDir/MobileSafari.app") {
+ return "$configurationProductDir/MobileSafari.app";
+ }
+ return installedMobileSafariBundle();
+}
+
+sub plistPathFromBundle($)
+{
+ my ($appBundle) = @_;
+ return "$appBundle/Info.plist" if -f "$appBundle/Info.plist"; # iOS app bundle
+ return "$appBundle/Contents/Info.plist" if -f "$appBundle/Contents/Info.plist"; # Mac app bundle
+ return "";
+}
+
+sub appIdentifierFromBundle($)
+{
+ my ($appBundle) = @_;
+ my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute.
+ chomp(my $bundleIdentifier = `defaults read '$plistPath' CFBundleIdentifier 2> /dev/null`);
+ return $bundleIdentifier;
+}
+
+sub appDisplayNameFromBundle($)
+{
+ my ($appBundle) = @_;
+ my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute.
+ chomp(my $bundleDisplayName = `defaults read '$plistPath' CFBundleDisplayName 2> /dev/null`);
+ return $bundleDisplayName;
+}
+
+sub waitUntilIOSSimulatorDeviceIsInState($$)
+{
+ my ($deviceUDID, $waitUntilState) = @_;
+ my $device = iosSimulatorDeviceByUDID($deviceUDID);
+ while ($device->{state} ne $waitUntilState) {
+ usleep(500 * 1000); # Waiting 500ms between file system polls does not make script run-safari feel sluggish.
+ $device = iosSimulatorDeviceByUDID($deviceUDID);
+ }
+}
+
+sub relaunchIOSSimulator($)
+{
+ my ($simulatedDevice) = @_;
+ quitIOSSimulator($simulatedDevice->{UDID});
+
+ # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator
+ my $iosSimulatorBundleID = "com.apple.iphonesimulator";
+ system("open", "-b", $iosSimulatorBundleID, "--args", "-CurrentDeviceUDID", $simulatedDevice->{UDID}) == 0 or die "Failed to open $iosSimulatorBundleID: $!";
+
+ waitUntilIOSSimulatorDeviceIsInState($simulatedDevice->{UDID}, SIMULATOR_DEVICE_STATE_BOOTED);
+}
+
+sub quitIOSSimulator(;$)
+{
+ my ($waitForShutdownOfSimulatedDeviceUDID) = @_;
+ # FIXME: <rdar://problem/20916140> Switch to using CoreSimulator.framework for launching and quitting iOS Simulator
+ exitStatus(system {"osascript"} "osascript", "-e", 'tell application id "com.apple.iphonesimulator" to quit') == 0 or die "Failed to quit iOS Simulator: $!";
+ if (!defined($waitForShutdownOfSimulatedDeviceUDID)) {
+ return;
+ }
+ # FIXME: We assume that $waitForShutdownOfSimulatedDeviceUDID was not booted using the simctl command line tool.
+ # Otherwise we will spin indefinitely since quiting the iOS Simulator will not shutdown this device. We
+ # should add a maximum time limit to wait for a device to shutdown and either return an error or die()
+ # on expiration of the time limit.
+ waitUntilIOSSimulatorDeviceIsInState($waitForShutdownOfSimulatedDeviceUDID, SIMULATOR_DEVICE_STATE_SHUTDOWN);
+}
+
+sub iosSimulatorDeviceByName($)
+{
+ my ($simulatorName) = @_;
+ my $simulatorRuntime = iosSimulatorRuntime();
+ my @devices = iOSSimulatorDevices();
+ for my $device (@devices) {
+ if ($device->{name} eq $simulatorName && $device->{runtime} eq $simulatorRuntime) {
+ return $device;
+ }
+ }
+ return undef;
+}
+
+sub iosSimulatorDeviceByUDID($)
+{
+ my ($simulatedDeviceUDID) = @_;
+ my $devicePlistPath = File::Spec->catfile(iOSSimulatorDevicesPath(), $simulatedDeviceUDID, "device.plist");
+ if (!-f $devicePlistPath) {
+ return;
+ }
+ # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known
+ # dictionary keys so as to decouple our representation of the plist from the actual structure
+ # of the plist, which may change.
+ eval "require Foundation";
+ return Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_($devicePlistPath));
+}
+
+sub iosSimulatorRuntime()
+{
+ my $xcodeSDKVersion = xcodeSDKVersion();
+ $xcodeSDKVersion =~ s/\./-/;
+ return "com.apple.CoreSimulator.SimRuntime.iOS-$xcodeSDKVersion";
+}
+
+sub findOrCreateSimulatorForIOSDevice($)
+{
+ my ($simulatorNameSuffix) = @_;
+ my $simulatorName;
+ my $simulatorDeviceType;
+ if (architecture() eq "x86_64") {
+ $simulatorName = "iPhone 5s " . $simulatorNameSuffix;
+ $simulatorDeviceType = "com.apple.CoreSimulator.SimDeviceType.iPhone-5s";
+ } else {
+ $simulatorName = "iPhone 5 " . $simulatorNameSuffix;
+ $simulatorDeviceType = "com.apple.CoreSimulator.SimDeviceType.iPhone-5";
+ }
+ my $simulatedDevice = iosSimulatorDeviceByName($simulatorName);
+ return $simulatedDevice if $simulatedDevice;
+ return createiOSSimulatorDevice($simulatorName, $simulatorDeviceType, iosSimulatorRuntime());
+}
+
+sub isIOSSimulatorSystemInstalledApp($)
+{
+ my ($appBundle) = @_;
+ my $simulatorApplicationsPath = realpath(iosSimulatorApplicationsPath());
+ return substr(realpath($appBundle), 0, length($simulatorApplicationsPath)) eq $simulatorApplicationsPath;
+}
+
+sub hasUserInstalledAppInSimulatorDevice($$)
+{
+ my ($appIdentifier, $simulatedDeviceUDID) = @_;
+ my $userInstalledAppPath = File::Spec->catfile($ENV{HOME}, "Library", "Developer", "CoreSimulator", "Devices", $simulatedDeviceUDID, "data", "Containers", "Bundle", "Application");
+ if (!-d $userInstalledAppPath) {
+ return 0; # No user installed apps.
+ }
+ local @::userInstalledAppBundles;
+ my $wantedFunction = sub {
+ my $file = $_;
+
+ # Ignore hidden files and directories.
+ if ($file =~ /^\../) {
+ $File::Find::prune = 1;
+ return;
+ }
+
+ return if !-d $file || $file !~ /\.app$/;
+ push @::userInstalledAppBundles, $File::Find::name;
+ $File::Find::prune = 1; # Do not traverse contents of app bundle.
+ };
+ find($wantedFunction, $userInstalledAppPath);
+ for my $userInstalledAppBundle (@::userInstalledAppBundles) {
+ if (appIdentifierFromBundle($userInstalledAppBundle) eq $appIdentifier) {
+ return 1; # Has user installed app.
+ }
+ }
+ return 0; # Does not have user installed app.
+}
+
+sub isSimulatorDeviceBooted($)
+{
+ my ($simulatedDeviceUDID) = @_;
+ my $device = iosSimulatorDeviceByUDID($simulatedDeviceUDID);
+ return $device && $device->{state} eq SIMULATOR_DEVICE_STATE_BOOTED;
+}
+
+sub runIOSWebKitAppInSimulator($;$)
+{
+ my ($appBundle, $simulatorOptions) = @_;
+ my $productDir = productDir();
+ my $appDisplayName = appDisplayNameFromBundle($appBundle);
+ my $appIdentifier = appIdentifierFromBundle($appBundle);
+ my $simulatedDevice = findOrCreateSimulatorForIOSDevice("For WebKit Development");
+ my $simulatedDeviceUDID = $simulatedDevice->{UDID};
+
+ my $willUseSystemInstalledApp = isIOSSimulatorSystemInstalledApp($appBundle);
+ if ($willUseSystemInstalledApp) {
+ if (hasUserInstalledAppInSimulatorDevice($appIdentifier, $simulatedDeviceUDID)) {
+ # Restore the system-installed app in the simulator device corresponding to $appBundle as it
+ # was previously overwritten with a custom built version of the app.
+ # FIXME: Only restore the system-installed version of the app instead of erasing all contents and settings.
+ print "Quitting iOS Simulator...\n";
+ quitIOSSimulator($simulatedDeviceUDID);
+ print "Erasing contents and settings for simulator device \"$simulatedDevice->{name}\".\n";
+ exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "erase", $simulatedDeviceUDID)) == 0 or die;
+ }
+ # FIXME: We assume that if $simulatedDeviceUDID is not booted then iOS Simulator is not open. However
+ # $simulatedDeviceUDID may have been booted using the simctl command line tool. If $simulatedDeviceUDID
+ # was booted using simctl then we should shutdown the device and launch iOS Simulator to boot it again.
+ if (!isSimulatorDeviceBooted($simulatedDeviceUDID)) {
+ print "Launching iOS Simulator...\n";
+ relaunchIOSSimulator($simulatedDevice);
+ }
+ } else {
+ # FIXME: We should killall(1) any running instances of $appBundle before installing it to ensure
+ # that simctl launch opens the latest installed version of the app. For now we quit and
+ # launch the iOS Simulator again to ensure there are no running instances of $appBundle.
+ print "Quitting and launching iOS Simulator...\n";
+ relaunchIOSSimulator($simulatedDevice);
+
+ print "Installing $appBundle.\n";
+ # Install custom built app, overwriting an app with the same app identifier if one exists.
+ exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "install", $simulatedDeviceUDID, $appBundle)) == 0 or die;
+
+ }
+
+ $simulatorOptions = {} unless $simulatorOptions;
+
+ my %simulatorENV;
+ %simulatorENV = %{$simulatorOptions->{applicationEnvironment}} if $simulatorOptions->{applicationEnvironment};
+ {
+ local %ENV; # Shadow global-scope %ENV so that changes to it will not be seen outside of this scope.
+ setupIOSWebKitEnvironment($productDir);
+ %simulatorENV = %ENV;
+ }
+ my $applicationArguments = \@ARGV;
+ $applicationArguments = $simulatorOptions->{applicationArguments} if $simulatorOptions && $simulatorOptions->{applicationArguments};
+
+ # Prefix the environment variables with SIMCTL_CHILD_ per `xcrun simctl help launch`.
+ foreach my $key (keys %simulatorENV) {
+ $ENV{"SIMCTL_CHILD_$key"} = $simulatorENV{$key};
+ }
+
+ print "Starting $appDisplayName with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
+ return exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "launch", $simulatedDeviceUDID, $appIdentifier, @$applicationArguments));
+}
+
+sub runIOSWebKitApp($)
+{
+ my ($appBundle) = @_;
+ if (willUseIOSDeviceSDKWhenBuilding()) {
+ die "Only running Safari in iOS Simulator is supported now.";
+ }
+ if (willUseIOSSimulatorSDKWhenBuilding()) {
+ return runIOSWebKitAppInSimulator($appBundle);
+ }
+ die "Not using an iOS SDK."
+}
+
+sub runMacWebKitApp($;$)
+{
+ my ($appPath, $useOpenCommand) = @_;
+ my $productDir = productDir();
+ print "Starting @{[basename($appPath)]} with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
+
+ local %ENV = %ENV;
+ setupMacWebKitEnvironment($productDir);
+
+ if (defined($useOpenCommand) && $useOpenCommand == USE_OPEN_COMMAND) {
+ return system("open", "-W", "-a", $appPath, "--args", argumentsForRunAndDebugMacWebKitApp());
+ }
+ if (architecture()) {
+ return system "arch", "-" . architecture(), $appPath, argumentsForRunAndDebugMacWebKitApp();
+ }
+ return system { $appPath } $appPath, argumentsForRunAndDebugMacWebKitApp();
+}
+
+sub execMacWebKitAppForDebugging($)
+{
+ my ($appPath) = @_;
+ my $architectureSwitch;
+ my $argumentsSeparator;
+
+ if (debugger() eq "lldb") {
+ $architectureSwitch = "--arch";
+ $argumentsSeparator = "--";
+ } elsif (debugger() eq "gdb") {
+ $architectureSwitch = "-arch";
+ $argumentsSeparator = "--args";
+ } else {
+ die "Unknown debugger $debugger.\n";
+ }
+
+ my $debuggerPath = `xcrun -find $debugger`;
+ chomp $debuggerPath;
+ die "Can't find the $debugger executable.\n" unless -x $debuggerPath;
+
+ my $productDir = productDir();
+ setupMacWebKitEnvironment($productDir);
+
+ my @architectureFlags = ($architectureSwitch, architecture());
+ if (!shouldTargetWebProcess()) {
+ print "Starting @{[basename($appPath)]} under $debugger with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
+ exec { $debuggerPath } $debuggerPath, @architectureFlags, $argumentsSeparator, $appPath, argumentsForRunAndDebugMacWebKitApp() or die;
+ } else {
+ if (shouldUseXPCServiceForWebProcess()) {
+ die "Targeting the Web Process is not compatible with using an XPC Service for the Web Process at this time.";
+ }
+
+ my $webProcessShimPath = File::Spec->catfile($productDir, "SecItemShim.dylib");
+ my $webProcessPath = File::Spec->catdir($productDir, "WebProcess.app");
+ my $webKit2ExecutablePath = File::Spec->catfile($productDir, "WebKit2.framework", "WebKit2");
+
+ appendToEnvironmentVariableList("DYLD_INSERT_LIBRARIES", $webProcessShimPath);
+
+ print "Starting WebProcess under $debugger with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
+ exec { $debuggerPath } $debuggerPath, @architectureFlags, $argumentsSeparator, $webProcessPath, $webKit2ExecutablePath, "-type", "webprocess", "-client-executable", $appPath or die;
+ }
+}
+
+sub debugSafari
+{
+ if (isAppleMacWebKit()) {
+ checkFrameworks();
+ execMacWebKitAppForDebugging(safariPath());
+ }
+
+ return 1; # Unsupported platform; can't debug Safari on this platform.
+}
+
+sub runSafari
+{
+ if (isIOSWebKit()) {
+ return runIOSWebKitApp(mobileSafariBundle());
+ }
+
+ if (isAppleMacWebKit()) {
+ return runMacWebKitApp(safariPath());
+ }
+
+ if (isAppleWinWebKit()) {
+ my $result;
+ my $productDir = productDir();
+ my $webKitLauncherPath = File::Spec->catfile(productDir(), "WinLauncher.exe");
+ return system { $webKitLauncherPath } $webKitLauncherPath, @ARGV;
+ }
+
+ return 1; # Unsupported platform; can't run Safari on this platform.
+}
+
+sub runMiniBrowser
+{
+ if (isAppleMacWebKit()) {
+ return runMacWebKitApp(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
+ }
+
+ return 1;
+}
+
+sub debugMiniBrowser
+{
+ if (isAppleMacWebKit()) {
+ execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "MiniBrowser.app", "Contents", "MacOS", "MiniBrowser"));
+ }
+
+ return 1;
+}
+
+sub runWebKitTestRunner
+{
+ if (isAppleMacWebKit()) {
+ return runMacWebKitApp(File::Spec->catfile(productDir(), "WebKitTestRunner"));
+ }
+
+ return 1;
+}
+
+sub debugWebKitTestRunner
+{
+ if (isAppleMacWebKit()) {
+ execMacWebKitAppForDebugging(File::Spec->catfile(productDir(), "WebKitTestRunner"));
+ }
+
+ return 1;
+}
+
+sub readRegistryString
+{
+ my ($valueName) = @_;
+ chomp(my $string = `regtool --wow32 get "$valueName"`);
+ return $string;
+}
+
+sub writeRegistryString
+{
+ my ($valueName, $string) = @_;
+
+ my $error = system "regtool", "--wow32", "set", "-s", $valueName, $string;
+
+ # On Windows Vista/7 with UAC enabled, regtool will fail to modify the registry, but will still
+ # return a successful exit code. So we double-check here that the value we tried to write to the
+ # registry was really written.
+ return !$error && readRegistryString($valueName) eq $string;
+}
+
+sub formatBuildTime($)
+{
+ my ($buildTime) = @_;
+
+ my $buildHours = int($buildTime / 3600);
+ my $buildMins = int(($buildTime - $buildHours * 3600) / 60);
+ my $buildSecs = $buildTime - $buildHours * 3600 - $buildMins * 60;
+
+ if ($buildHours) {
+ return sprintf("%dh:%02dm:%02ds", $buildHours, $buildMins, $buildSecs);
+ }
+ return sprintf("%02dm:%02ds", $buildMins, $buildSecs);
+}
+
+sub runSvnUpdateAndResolveChangeLogs(@)
+{
+ my @svnOptions = @_;
+ my $openCommand = "svn update " . join(" ", @svnOptions);
+ open my $update, "$openCommand |" or die "cannot execute command $openCommand";
+ my @conflictedChangeLogs;
+ while (my $line = <$update>) {
+ print $line;
+ $line =~ m/^C\s+(.+?)[\r\n]*$/;
+ if ($1) {
+ my $filename = normalizePath($1);
+ push @conflictedChangeLogs, $filename if basename($filename) eq "ChangeLog";
+ }
+ }
+ close $update or die;
+
+ if (@conflictedChangeLogs) {
+ print "Attempting to merge conflicted ChangeLogs.\n";
+ my $resolveChangeLogsPath = File::Spec->catfile(sourceDir(), "Tools", "Scripts", "resolve-ChangeLogs");
+ (system($resolveChangeLogsPath, "--no-warnings", @conflictedChangeLogs) == 0)
+ or die "Could not open resolve-ChangeLogs script: $!.\n";
+ }
+}
+
+sub runGitUpdate()
+{
+ # Doing a git fetch first allows setups with svn-remote.svn.fetch = trunk:refs/remotes/origin/master
+ # to perform the rebase much much faster.
+ system("git", "fetch");
+ if (isGitSVNDirectory(".")) {
+ system("git", "svn", "rebase") == 0 or die;
+ } else {
+ # This will die if branch.$BRANCHNAME.merge isn't set, which is
+ # almost certainly what we want.
+ system("git", "pull") == 0 or die;
+ }
+}
+
+1;
diff --git a/Tools/TestWebKitAPI/CMakeLists.txt b/Tools/TestWebKitAPI/CMakeLists.txt
new file mode 100644
index 000000000..b5e5904fb
--- /dev/null
+++ b/Tools/TestWebKitAPI/CMakeLists.txt
@@ -0,0 +1,216 @@
+set(TESTWEBKITAPI_DIR "${TOOLS_DIR}/TestWebKitAPI")
+set(test_wtf_LIBRARIES
+ WTF
+ gtest
+)
+
+set(test_webcore_LIBRARIES
+ WTF
+ WebCore
+ gtest
+)
+
+set(TestWebKitAPI_LIBRARIES
+ WTF
+)
+
+if (ENABLE_WEBKIT2)
+ set(test_webkit2_api_LIBRARIES
+ JavaScriptCore
+ TestWebKitAPIBase
+ WTF
+ WebKit2
+ gtest
+ )
+ list(APPEND TestWebKitAPI_LIBRARIES
+ WebKit2
+ )
+else ()
+ list(APPEND TestWebKitAPI_LIBRARIES
+ WebKit
+ )
+endif ()
+
+
+set(TestJavaScriptCore_LIBRARIES
+ JavaScriptCore
+ gtest
+)
+
+set(TestWTF_SOURCES
+ ${TESTWEBKITAPI_DIR}/Counters.cpp
+ ${TESTWEBKITAPI_DIR}/TestsController.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/AtomicString.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/CString.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/Condition.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/CheckedArithmeticOperations.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/DateMath.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/Deque.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/Functional.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/HashMap.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/HashSet.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/IntegerToStringConversion.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/ListHashSet.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/Lock.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/MD5.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/MathExtras.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/MediaTime.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/MetaAllocator.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/NakedPtr.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/ParkingLot.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/RedBlackTree.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/Ref.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/RefPtr.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/SHA1.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/SaturatedArithmeticOperations.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/StringBuilder.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/StringHasher.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/StringImpl.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/StringOperators.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/StringView.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/TemporaryChange.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/Vector.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/WTFString.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/WorkQueue.cpp
+)
+
+WEBKIT_INCLUDE_CONFIG_FILES_IF_EXISTS()
+
+include_directories(
+ ${TESTWEBKITAPI_DIR}
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/Source
+ ${DERIVED_SOURCES_JAVASCRIPTCORE_DIR}
+ ${DERIVED_SOURCES_WEBCORE_DIR}
+ ${JAVASCRIPTCORE_DIR}
+ ${JAVASCRIPTCORE_DIR}/API
+ ${JAVASCRIPTCORE_DIR}/ForwardingHeaders
+ ${THIRDPARTY_DIR}/gtest/include
+ ${WEBCORE_DIR}/css
+ ${WEBCORE_DIR}/dom
+ ${WEBCORE_DIR}/editing
+ ${WEBCORE_DIR}/loader/cache
+ ${WEBCORE_DIR}/platform
+ ${WEBCORE_DIR}/platform/animation
+ ${WEBCORE_DIR}/platform/graphics
+ ${WEBCORE_DIR}/platform/text
+ ${WEBCORE_DIR}/platform/network
+ ${WEBCORE_DIR}/platform/network/soup
+ ${WEBCORE_DIR}/rendering/style
+ ${WEBKIT2_DIR}/Platform/IPC
+ ${WEBKIT2_DIR}/Shared
+ ${WEBKIT2_DIR}/Shared/API
+ ${WEBKIT2_DIR}/Shared/API/c
+ ${WEBKIT2_DIR}/Shared/Plugins
+ ${WEBKIT2_DIR}/UIProcess
+ ${WEBKIT2_DIR}/UIProcess/API
+ ${WEBKIT2_DIR}/WebProcess/InjectedBundle
+ ${WEBKIT2_DIR}/WebProcess/InjectedBundle/API/c
+ ${WTF_DIR}
+)
+
+if (ENABLE_WEBKIT2)
+ add_library(TestWebKitAPIInjectedBundle SHARED
+ ${bundle_harness_SOURCES}
+ ${TESTWEBKITAPI_DIR}/InjectedBundleController.cpp
+ ${TESTWEBKITAPI_DIR}/InjectedBundleMain.cpp
+ ${TESTWEBKITAPI_DIR}/PlatformUtilities.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/CanHandleRequest_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/DidAssociateFormControls_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/DOMWindowExtensionBasic_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/DOMWindowExtensionNoCache_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/DocumentStartUserScriptAlertCrash_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/GetInjectedBundleInitializationUserDataCallback_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/HitTestResultNodeHandle_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/InjectedBundleBasic_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/InjectedBundleFrameHitTest_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/InjectedBundleInitializationUserDataCallbackWins_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/LoadCanceledNoServerRedirectCallback_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/MouseMoveAfterCrash_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFails_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutForImages_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFrames_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/NewFirstVisuallyNonEmptyLayout_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/ParentFrame_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/ResponsivenessTimerDoesntFireEarly_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/ShouldGoToBackForwardListItem_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/TextFieldDidBeginAndEndEditing_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/UserMessage_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WillLoad_Bundle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WillSendSubmitEvent_Bundle.cpp
+ )
+
+ target_link_libraries(TestWebKitAPIInjectedBundle ${TestWebKitAPI_LIBRARIES})
+ add_dependencies(TestWebKitAPIInjectedBundle ${ForwardingHeadersForTestWebKitAPI_NAME})
+
+ get_property(TestWebKitAPIInjectedBundle_PATH TARGET TestWebKitAPIInjectedBundle PROPERTY LOCATION)
+endif ()
+
+add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY=1 -DGTEST_HAS_RTTI=0
+ -DTEST_WEBKIT2_RESOURCES_DIR=\"${TESTWEBKITAPI_DIR}/Tests/WebKit2\"
+ -DTEST_INJECTED_BUNDLE_PATH=\"${TestWebKitAPIInjectedBundle_PATH}\"
+)
+
+# FIXME: This works around compatibility problems in the old version of the third-pary
+# googletest source code checkout. It should be removed once we upgrade to a newer version.
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ add_definitions(-DGTEST_HAS_TR1_TUPLE=0)
+endif ()
+
+add_executable(TestWTF
+ ${test_main_SOURCES}
+ ${TestWTF_SOURCES}
+)
+
+target_link_libraries(TestWTF ${test_wtf_LIBRARIES})
+add_dependencies(TestWTF WTF ${ForwardingHeadersForTestWebKitAPI_NAME} ${ForwardingNetworkHeadersForTestWebKitAPI_NAME})
+add_test(TestWTF ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY_WTF}/TestWTF)
+set_tests_properties(TestWTF PROPERTIES TIMEOUT 60)
+set_target_properties(TestWTF PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY_WTF}
+)
+
+# FIXME: EFL is the only port that separates the WebCore binaries. Each port ought to do closer to the same thing.
+foreach (testName ${test_webcore_BINARIES})
+ add_executable(${testName} ${test_main_SOURCES} ${TESTWEBKITAPI_DIR}/TestsController.cpp ${TESTWEBKITAPI_DIR}/Tests/WebCore/${testName}.cpp)
+ add_test(${testName} ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebCore/${testName})
+ set_tests_properties(${testName} PROPERTIES TIMEOUT 60)
+ target_link_libraries(${testName} ${test_webcore_LIBRARIES})
+ set_target_properties(${testName} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebCore
+ )
+endforeach ()
+
+if (ENABLE_WEBKIT2)
+ add_library(TestWebKitAPIBase
+ ${test_main_SOURCES}
+ ${webkit2_api_harness_SOURCES}
+ ${TESTWEBKITAPI_DIR}/JavaScriptTest.cpp
+ ${TESTWEBKITAPI_DIR}/PlatformUtilities.cpp
+ ${TESTWEBKITAPI_DIR}/TestsController.cpp
+ )
+
+ add_dependencies(TestWebKitAPIBase WebKit2 ${ForwardingHeadersForTestWebKitAPI_NAME} ${ForwardingNetworkHeadersForTestWebKitAPI_NAME})
+
+ foreach (testName ${test_webkit2_api_BINARIES})
+ get_filename_component(testBaseName ${testName} NAME)
+ add_executable(${testBaseName} ${TESTWEBKITAPI_DIR}/Tests/WebKit2/${testName}.cpp)
+ add_test(${testBaseName} ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebKit2/${testBaseName})
+ set_tests_properties(${testBaseName} PROPERTIES TIMEOUT 60)
+ target_link_libraries(${testBaseName} ${test_webkit2_api_LIBRARIES})
+ set_target_properties(${testBaseName} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebKit2
+ )
+ endforeach ()
+
+ # We don't run tests that are expected to fail. We could use the WILL_FAIL
+ # property, but it reports failure when the test crashes or timeouts and would
+ # make the bot red.
+ foreach (testName ${test_webkit2_api_fail_BINARIES})
+ add_executable(${testName} ${TESTWEBKITAPI_DIR}/Tests/WebKit2/${testName}.cpp)
+ target_link_libraries(${testName} ${test_webkit2_api_LIBRARIES})
+ set_target_properties(${testName} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebKit2/failure
+ )
+ endforeach ()
+endif ()
diff --git a/Tools/TestWebKitAPI/Configurations/TestWebKitAPICFLite.vsprops b/Tools/TestWebKitAPI/Configurations/TestWebKitAPICFLite.vsprops
new file mode 100644
index 000000000..61b661413
--- /dev/null
+++ b/Tools/TestWebKitAPI/Configurations/TestWebKitAPICFLite.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="TestWebKitAPICFLite"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="CFLite$(LibraryConfigSuffix).lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/Tools/TestWebKitAPI/Configurations/TestWebKitAPICFNetwork.vsprops b/Tools/TestWebKitAPI/Configurations/TestWebKitAPICFNetwork.vsprops
new file mode 100644
index 000000000..cc25b8669
--- /dev/null
+++ b/Tools/TestWebKitAPI/Configurations/TestWebKitAPICFNetwork.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="TestWebKitAPICFNetwork"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="CFNetwork$(LibraryConfigSuffix).lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/Tools/TestWebKitAPI/Configurations/TestWebKitAPICommon.vsprops b/Tools/TestWebKitAPI/Configurations/TestWebKitAPICommon.vsprops
new file mode 100644
index 000000000..57ec3506e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Configurations/TestWebKitAPICommon.vsprops
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="TestWebKitAPICommon"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(ProjectDir)&quot;;&quot;$(ProjectDir)\..&quot;;&quot;$(ConfigurationBuildDir)\include&quot;;&quot;$(ConfigurationBuildDir)\include\private\JavaScriptCore&quot;;&quot;$(ConfigurationBuildDir)\include\WebCore\ForwardingHeaders&quot;;&quot;$(ConfigurationBuildDir)\include\private&quot;;&quot;$(WebKitLibrariesDir)\include&quot;;&quot;$(ProjectDir)\..\..\..\Source\ThirdParty\gtest\include&quot;"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="WebKit$(WebKitDLLConfigSuffix).lib JavaScriptCore$(WebKitDLLConfigSuffix).lib gtest.lib"
+ SubSystem="1"
+ />
+</VisualStudioPropertySheet>
diff --git a/Tools/TestWebKitAPI/Configurations/TestWebKitAPICoreFoundation.vsprops b/Tools/TestWebKitAPI/Configurations/TestWebKitAPICoreFoundation.vsprops
new file mode 100644
index 000000000..ee139c0bc
--- /dev/null
+++ b/Tools/TestWebKitAPI/Configurations/TestWebKitAPICoreFoundation.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="TestWebKitAPICoreFoundation"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="CoreFoundation$(LibraryConfigSuffix).lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebug.vsprops b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebug.vsprops
new file mode 100644
index 000000000..981225f35
--- /dev/null
+++ b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebug.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="TestWebKitAPIDebug"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug.vsprops;.\TestWebKitAPICommon.vsprops;.\TestWebKitAPICoreFoundation.vsprops;.\TestWebKitAPICFNetwork.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebugAll.vsprops b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebugAll.vsprops
new file mode 100644
index 000000000..f70863b49
--- /dev/null
+++ b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebugAll.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="TestWebKitAPIDebugAll"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug_all.vsprops;.\TestWebKitAPICommon.vsprops;.\TestWebKitAPICoreFoundation.vsprops;.\TestWebKitAPICFNetwork.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebugCairoCFLite.vsprops b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebugCairoCFLite.vsprops
new file mode 100644
index 000000000..ba38ece12
--- /dev/null
+++ b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIDebugCairoCFLite.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="TestWebKitAPIDebugCairoCFLite"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\debug_wincairo.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\WinCairo.vsprops;..\Configurations\TestWebKitAPICommon.vsprops;..\Configurations\TestWebKitAPICFLite.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/TestWebKitAPI/Configurations/TestWebKitAPIInjectedBundleCommon.vsprops b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIInjectedBundleCommon.vsprops
new file mode 100644
index 000000000..5e19c4621
--- /dev/null
+++ b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIInjectedBundleCommon.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="TestWebKitAPIInjectedBundleCommon"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)\$(ProjectName)$(WebKitDLLConfigSuffix).dll"
+ />
+</VisualStudioPropertySheet>
diff --git a/Tools/TestWebKitAPI/Configurations/TestWebKitAPIRelease.vsprops b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIRelease.vsprops
new file mode 100644
index 000000000..e5edd81d8
--- /dev/null
+++ b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIRelease.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="TestWebKitAPIRelease"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\releaseproduction.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\release.vsprops;.\TestWebKitAPICommon.vsprops;.\TestWebKitAPICoreFoundation.vsprops;.\TestWebKitAPICFNetwork.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/TestWebKitAPI/Configurations/TestWebKitAPIReleaseCairoCFLite.vsprops b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIReleaseCairoCFLite.vsprops
new file mode 100644
index 000000000..1b97b644d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Configurations/TestWebKitAPIReleaseCairoCFLite.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="TestWebKitAPIReleaseCairoCFLite"
+ InheritedPropertySheets="$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\common.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\releaseproduction.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\release.vsprops;$(WebKitVSPropsRedirectionDir)..\..\..\WebKitLibraries\win\tools\vsprops\WinCairo.vsprops;..\Configurations\TestWebKitAPICommon.vsprops;..\Configurations\TestWebKitAPICFLite.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/Tools/TestWebKitAPI/Counters.cpp b/Tools/TestWebKitAPI/Counters.cpp
new file mode 100644
index 000000000..87aa4bba2
--- /dev/null
+++ b/Tools/TestWebKitAPI/Counters.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Counters.h"
+
+unsigned CopyMoveCounter::constructionCount = 0;
+unsigned CopyMoveCounter::copyCount = 0;
+unsigned CopyMoveCounter::moveCount = 0;
+
+unsigned ConstructorDestructorCounter::constructionCount = 0;
+unsigned ConstructorDestructorCounter::destructionCount = 0;
diff --git a/Tools/TestWebKitAPI/Counters.h b/Tools/TestWebKitAPI/Counters.h
new file mode 100644
index 000000000..1cfd7b71d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Counters.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Counters_h
+#define Counters_h
+
+struct CopyMoveCounter {
+ static unsigned constructionCount;
+ static unsigned copyCount;
+ static unsigned moveCount;
+
+ struct TestingScope {
+ TestingScope()
+ {
+ constructionCount = 0;
+ copyCount = 0;
+ moveCount = 0;
+ }
+ };
+
+ CopyMoveCounter() { constructionCount++; }
+ CopyMoveCounter(const CopyMoveCounter&) { copyCount++; }
+ CopyMoveCounter& operator=(const CopyMoveCounter&) { copyCount++; return *this; }
+ CopyMoveCounter(CopyMoveCounter&&) { moveCount++; }
+ CopyMoveCounter& operator=(CopyMoveCounter&&) { moveCount++; return *this; }
+};
+
+
+struct ConstructorDestructorCounter {
+ static unsigned constructionCount;
+ static unsigned destructionCount;
+
+ struct TestingScope {
+ TestingScope()
+ {
+ constructionCount = 0;
+ destructionCount = 0;
+ }
+ };
+
+ ConstructorDestructorCounter() { constructionCount++; }
+ ~ConstructorDestructorCounter() { destructionCount++; }
+};
+
+template<typename T>
+struct DeleterCounter {
+ static unsigned deleterCount;
+
+ struct TestingScope {
+ TestingScope()
+ {
+ deleterCount = 0;
+ }
+ };
+
+ void operator()(T* p) const
+ {
+ deleterCount++;
+ delete p;
+ }
+};
+
+template<class T> unsigned DeleterCounter<T>::deleterCount = 0;
+
+#endif // Counters_h
diff --git a/Tools/TestWebKitAPI/ForwardingHeaders/WebCore/LayoutUnit.h b/Tools/TestWebKitAPI/ForwardingHeaders/WebCore/LayoutUnit.h
new file mode 100644
index 000000000..de0a7454e
--- /dev/null
+++ b/Tools/TestWebKitAPI/ForwardingHeaders/WebCore/LayoutUnit.h
@@ -0,0 +1,4 @@
+#ifndef TestWebKitAPI_FWD_LayoutUnit_h
+#define TestWebKitAPI_FWD_LayoutUnit_h
+#include <LayoutUnit.h>
+#endif
diff --git a/Tools/TestWebKitAPI/InjectedBundleController.cpp b/Tools/TestWebKitAPI/InjectedBundleController.cpp
new file mode 100644
index 000000000..53f39a874
--- /dev/null
+++ b/Tools/TestWebKitAPI/InjectedBundleController.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleController.h"
+
+#include "InjectedBundleTest.h"
+#include "PlatformUtilities.h"
+#include <algorithm>
+#include <assert.h>
+
+namespace TestWebKitAPI {
+
+InjectedBundleController& InjectedBundleController::singleton()
+{
+ static InjectedBundleController& shared = *new InjectedBundleController;
+ return shared;
+}
+
+InjectedBundleController::InjectedBundleController()
+ : m_bundle(0)
+ , m_currentTest(0)
+{
+}
+
+void InjectedBundleController::initialize(WKBundleRef bundle, WKTypeRef initializationUserData)
+{
+ platformInitialize();
+
+ m_bundle = bundle;
+
+ if (!initializationUserData)
+ return;
+
+ WKBundleClientV1 client = {
+ { 0, this },
+ didCreatePage,
+ willDestroyPage,
+ didInitializePageGroup,
+ didReceiveMessage,
+ didReceiveMessageToPage
+ };
+ WKBundleSetClient(m_bundle, &client.base);
+
+ // Initialize the test from the "initializationUserData".
+
+ assert(WKGetTypeID(initializationUserData) == WKDictionaryGetTypeID());
+ WKDictionaryRef initializationDictionary = static_cast<WKDictionaryRef>(initializationUserData);
+
+ WKRetainPtr<WKStringRef> testNameKey(AdoptWK, WKStringCreateWithUTF8CString("TestName"));
+ WKStringRef testName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(initializationDictionary, testNameKey.get()));
+
+ WKRetainPtr<WKStringRef> userDataKey(AdoptWK, WKStringCreateWithUTF8CString("UserData"));
+ WKTypeRef userData = WKDictionaryGetItemForKey(initializationDictionary, userDataKey.get());
+ initializeTestNamed(bundle, Util::toSTD(testName), userData);
+}
+
+void InjectedBundleController::didCreatePage(WKBundleRef bundle, WKBundlePageRef page, const void* clientInfo)
+{
+ InjectedBundleController* self = static_cast<InjectedBundleController*>(const_cast<void*>(clientInfo));
+ assert(self->m_currentTest);
+ self->m_currentTest->didCreatePage(bundle, page);
+}
+
+void InjectedBundleController::willDestroyPage(WKBundleRef bundle, WKBundlePageRef page, const void* clientInfo)
+{
+ InjectedBundleController* self = static_cast<InjectedBundleController*>(const_cast<void*>(clientInfo));
+ assert(self->m_currentTest);
+ self->m_currentTest->willDestroyPage(bundle, page);
+}
+
+void InjectedBundleController::didInitializePageGroup(WKBundleRef bundle, WKBundlePageGroupRef pageGroup, const void* clientInfo)
+{
+ InjectedBundleController* self = static_cast<InjectedBundleController*>(const_cast<void*>(clientInfo));
+ assert(self->m_currentTest);
+ self->m_currentTest->didInitializePageGroup(bundle, pageGroup);
+}
+
+void InjectedBundleController::didReceiveMessage(WKBundleRef bundle, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ InjectedBundleController* self = static_cast<InjectedBundleController*>(const_cast<void*>(clientInfo));
+ assert(self->m_currentTest);
+ self->m_currentTest->didReceiveMessage(bundle, messageName, messageBody);
+}
+
+void InjectedBundleController::didReceiveMessageToPage(WKBundleRef bundle, WKBundlePageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ InjectedBundleController* self = static_cast<InjectedBundleController*>(const_cast<void*>(clientInfo));
+ assert(self->m_currentTest);
+ self->m_currentTest->didReceiveMessageToPage(bundle, page, messageName, messageBody);
+}
+
+void InjectedBundleController::dumpTestNames()
+{
+ std::map<std::string, CreateInjectedBundleTestFunction>::const_iterator it = m_createInjectedBundleTestFunctions.begin();
+ std::map<std::string, CreateInjectedBundleTestFunction>::const_iterator end = m_createInjectedBundleTestFunctions.end();
+ for (; it != end; ++it)
+ printf("%s\n", (*it).first.c_str());
+}
+
+void InjectedBundleController::initializeTestNamed(WKBundleRef bundle, const std::string& identifier, WKTypeRef userData)
+{
+ CreateInjectedBundleTestFunction createTestFunction = m_createInjectedBundleTestFunctions[identifier];
+ if (!createTestFunction) {
+ printf("ERROR: InjectedBundle test not found - %s\n", identifier.c_str());
+ exit(1);
+ }
+
+ m_currentTest = createTestFunction(identifier);
+ m_currentTest->initialize(bundle, userData);
+}
+
+void InjectedBundleController::registerCreateInjectedBundleTestFunction(const std::string& identifier, CreateInjectedBundleTestFunction function)
+{
+ m_createInjectedBundleTestFunctions[identifier] = function;
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/InjectedBundleController.h b/Tools/TestWebKitAPI/InjectedBundleController.h
new file mode 100644
index 000000000..eaa7f0d17
--- /dev/null
+++ b/Tools/TestWebKitAPI/InjectedBundleController.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef InjectedBundleController_h
+#define InjectedBundleController_h
+
+#include <WebKit/WKBundle.h>
+#include <map>
+#include <string>
+
+namespace TestWebKitAPI {
+
+class InjectedBundleTest;
+
+class InjectedBundleController {
+public:
+ static InjectedBundleController& singleton();
+
+ void initialize(WKBundleRef, WKTypeRef);
+
+ void dumpTestNames();
+ void initializeTestNamed(WKBundleRef bundle, const std::string&, WKTypeRef userData);
+
+ typedef InjectedBundleTest* (*CreateInjectedBundleTestFunction)(const std::string&);
+ void registerCreateInjectedBundleTestFunction(const std::string&, CreateInjectedBundleTestFunction);
+
+ WKBundleRef bundle() const { return m_bundle; }
+
+private:
+ InjectedBundleController();
+ ~InjectedBundleController();
+
+ void platformInitialize();
+
+ static void didCreatePage(WKBundleRef, WKBundlePageRef, const void* clientInfo);
+ static void willDestroyPage(WKBundleRef, WKBundlePageRef, const void* clientInfo);
+ static void didInitializePageGroup(WKBundleRef, WKBundlePageGroupRef, const void* clientInfo);
+ static void didReceiveMessage(WKBundleRef, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo);
+ static void didReceiveMessageToPage(WKBundleRef, WKBundlePageRef, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo);
+
+ std::map<std::string, CreateInjectedBundleTestFunction> m_createInjectedBundleTestFunctions;
+ WKBundleRef m_bundle;
+ InjectedBundleTest* m_currentTest;
+};
+
+} // namespace TestWebKitAPI
+
+#endif // InjectedBundleController_h
diff --git a/Tools/TestWebKitAPI/InjectedBundleMain.cpp b/Tools/TestWebKitAPI/InjectedBundleMain.cpp
new file mode 100644
index 000000000..b2537f737
--- /dev/null
+++ b/Tools/TestWebKitAPI/InjectedBundleMain.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleController.h"
+#include <WebKit/WKBundleInitialize.h>
+
+#if defined(WIN32) || defined(_WIN32)
+extern "C" __declspec(dllexport)
+#else
+extern "C"
+#endif
+void WKBundleInitialize(WKBundleRef bundle, WKTypeRef initializationUserData)
+{
+ TestWebKitAPI::InjectedBundleController::singleton().initialize(bundle, initializationUserData);
+}
+
+#endif
diff --git a/Tools/TestWebKitAPI/InjectedBundleTest.h b/Tools/TestWebKitAPI/InjectedBundleTest.h
new file mode 100644
index 000000000..ad2cb9581
--- /dev/null
+++ b/Tools/TestWebKitAPI/InjectedBundleTest.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef InjectedBundleTest_h
+#define InjectedBundleTest_h
+
+#include "InjectedBundleController.h"
+
+namespace TestWebKitAPI {
+
+class InjectedBundleTest {
+public:
+ virtual ~InjectedBundleTest() { }
+
+ virtual void initialize(WKBundleRef, WKTypeRef) { }
+
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef) { }
+ virtual void willDestroyPage(WKBundleRef, WKBundlePageRef) { }
+ virtual void didInitializePageGroup(WKBundleRef, WKBundlePageGroupRef) { }
+ virtual void didReceiveMessage(WKBundleRef, WKStringRef messageName, WKTypeRef messageBody) { }
+ virtual void didReceiveMessageToPage(WKBundleRef, WKBundlePageRef, WKStringRef messageName, WKTypeRef messageBody) { }
+
+ std::string name() const { return m_identifier; }
+
+ template<typename TestClassTy> class Register {
+ public:
+ Register(const std::string& test)
+ {
+ InjectedBundleController::singleton().registerCreateInjectedBundleTestFunction(test, Register::create);
+ }
+
+ private:
+ static InjectedBundleTest* create(const std::string& identifier)
+ {
+ return new TestClassTy(identifier);
+ }
+ };
+
+protected:
+ InjectedBundleTest(const std::string& identifier)
+ : m_identifier(identifier)
+ {
+ }
+
+ std::string m_identifier;
+};
+
+} // namespace TestWebKitAPI
+
+#endif // InjectedBundleTest_h
diff --git a/Tools/TestWebKitAPI/JavaScriptTest.cpp b/Tools/TestWebKitAPI/JavaScriptTest.cpp
new file mode 100644
index 000000000..133d99eee
--- /dev/null
+++ b/Tools/TestWebKitAPI/JavaScriptTest.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "JavaScriptTest.h"
+
+#include "PlatformUtilities.h"
+#include "Test.h"
+#include <JavaScriptCore/JSContextRef.h>
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <WebKit/WKRetainPtr.h>
+#include <WebKit/WKSerializedScriptValue.h>
+#include <wtf/StdLibExtras.h>
+
+namespace TestWebKitAPI {
+
+struct JavaScriptCallbackContext {
+ JavaScriptCallbackContext() : didFinish(false) { }
+
+ bool didFinish;
+ JSRetainPtr<JSStringRef> actualString;
+};
+
+static void javaScriptCallback(WKSerializedScriptValueRef resultSerializedScriptValue, WKErrorRef error, void* ctx)
+{
+ ASSERT_NOT_NULL(resultSerializedScriptValue);
+
+ JavaScriptCallbackContext* context = static_cast<JavaScriptCallbackContext*>(ctx);
+
+ JSGlobalContextRef scriptContext = JSGlobalContextCreate(0);
+ ASSERT_NOT_NULL(scriptContext);
+
+ JSValueRef scriptValue = WKSerializedScriptValueDeserialize(resultSerializedScriptValue, scriptContext, 0);
+ ASSERT_NOT_NULL(scriptValue);
+
+ context->actualString.adopt(JSValueToStringCopy(scriptContext, scriptValue, 0));
+ ASSERT_NOT_NULL(context->actualString.get());
+
+ context->didFinish = true;
+
+ JSGlobalContextRelease(scriptContext);
+
+ EXPECT_NULL(error);
+}
+
+::testing::AssertionResult runJSTest(const char*, const char*, const char*, WKPageRef page, const char* script, const char* expectedResult)
+{
+ JavaScriptCallbackContext context;
+ WKPageRunJavaScriptInMainFrame(page, Util::toWK(script).get(), &context, javaScriptCallback);
+ Util::run(&context.didFinish);
+
+ size_t bufferSize = JSStringGetMaximumUTF8CStringSize(context.actualString.get());
+ auto buffer = std::make_unique<char[]>(bufferSize);
+ JSStringGetUTF8CString(context.actualString.get(), buffer.get(), bufferSize);
+
+ return compareJSResult(script, buffer.get(), expectedResult);
+}
+
+::testing::AssertionResult compareJSResult(const char* script, const char* actualResult, const char* expectedResult)
+{
+ if (!strcmp(actualResult, expectedResult))
+ return ::testing::AssertionSuccess();
+
+ return ::testing::AssertionFailure()
+ << "JS expression: " << script << "\n"
+ << " Actual: " << actualResult << "\n"
+ << " Expected: " << expectedResult;
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/JavaScriptTest.h b/Tools/TestWebKitAPI/JavaScriptTest.h
new file mode 100644
index 000000000..c0494b2ca
--- /dev/null
+++ b/Tools/TestWebKitAPI/JavaScriptTest.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if PLATFORM(COCOA)
+OBJC_CLASS WKView;
+OBJC_CLASS WebView;
+#endif
+
+namespace TestWebKitAPI {
+
+// These macros execute |script| in the page and wait until it has run, then compare its return
+// value to the expected result.
+#define EXPECT_JS_EQ(page, script, result) EXPECT_PRED_FORMAT3(runJSTest, page, script, result)
+#define EXPECT_JS_FALSE(page, script) EXPECT_JS_EQ(page, script, "false")
+#define EXPECT_JS_TRUE(page, script) EXPECT_JS_EQ(page, script, "true")
+
+::testing::AssertionResult runJSTest(const char* pageExpr, const char* scriptExpr, const char* expectedResultExpr, WKPageRef, const char* script, const char* expectedResult);
+::testing::AssertionResult compareJSResult(const char* script, const char* actualResult, const char* expectedResult);
+
+#if PLATFORM(COCOA)
+::testing::AssertionResult runJSTest(const char* webViewExpr, const char* scriptExpr, const char* expectedResultExpr, WebView *, const char* script, const char* expectedResult);
+::testing::AssertionResult runJSTest(const char* viewExpr, const char* scriptExpr, const char* expectedResultExpr, WKView *, const char* script, const char* expectedResult);
+#endif
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/PlatformGTK.cmake b/Tools/TestWebKitAPI/PlatformGTK.cmake
new file mode 100644
index 000000000..8e5c4af6c
--- /dev/null
+++ b/Tools/TestWebKitAPI/PlatformGTK.cmake
@@ -0,0 +1,164 @@
+set(TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/TestWebKitAPI")
+set(TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY_WTF "${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WTF")
+
+# This is necessary because it is possible to build TestWebKitAPI with WebKit2
+# disabled and this triggers the inclusion of the WebKit2 headers.
+add_definitions(-DBUILDING_WEBKIT2__)
+
+add_custom_target(TestWebKitAPI-forwarding-headers
+ COMMAND ${PERL_EXECUTABLE} ${WEBKIT2_DIR}/Scripts/generate-forwarding-headers.pl --include-path ${TESTWEBKITAPI_DIR} --output ${FORWARDING_HEADERS_DIR} --platform gtk --platform soup
+ DEPENDS WebKit2-forwarding-headers
+)
+
+set(ForwardingHeadersForTestWebKitAPI_NAME TestWebKitAPI-forwarding-headers)
+
+include_directories(
+ ${FORWARDING_HEADERS_DIR}
+ ${FORWARDING_HEADERS_DIR}/JavaScriptCore
+ ${WEBKIT2_DIR}/UIProcess/API/C/soup
+ ${WEBKIT2_DIR}/UIProcess/API/C/gtk
+ ${WEBKIT2_DIR}/UIProcess/API/gtk
+)
+
+include_directories(SYSTEM
+ ${GDK3_INCLUDE_DIRS}
+ ${GLIB_INCLUDE_DIRS}
+ ${GTK3_INCLUDE_DIRS}
+ ${LIBSOUP_INCLUDE_DIRS}
+)
+
+set(test_main_SOURCES
+ ${TESTWEBKITAPI_DIR}/gtk/main.cpp
+)
+
+set(bundle_harness_SOURCES
+ ${TESTWEBKITAPI_DIR}/gtk/InjectedBundleControllerGtk.cpp
+ ${TESTWEBKITAPI_DIR}/gtk/PlatformUtilitiesGtk.cpp
+)
+
+set(webkit2_api_harness_SOURCES
+ ${TESTWEBKITAPI_DIR}/gtk/PlatformUtilitiesGtk.cpp
+ ${TESTWEBKITAPI_DIR}/gtk/PlatformWebViewGtk.cpp
+)
+
+list(APPEND test_wtf_LIBRARIES
+ ${GDK3_LIBRARIES}
+ ${GTK3_LIBRARIES}
+)
+
+list(APPEND test_webkit2_api_LIBRARIES
+ ${GDK3_LIBRARIES}
+ ${GTK3_LIBRARIES}
+)
+
+list(APPEND test_webcore_LIBRARIES
+ WebCorePlatformGTK
+ ${GDK3_LIBRARIES}
+ ${GTK3_LIBRARIES}
+)
+
+list(APPEND TestWebKitAPI_LIBRARIES
+ ${GDK3_LIBRARIES}
+ ${GTK3_LIBRARIES}
+)
+
+list(APPEND TestJavaScriptCore_LIBRARIES
+ ${GDK3_LIBRARIES}
+ ${GTK3_LIBRARIES}
+)
+
+add_executable(TestWebKit2
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/AboutBlankLoad.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/CanHandleRequest.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/CookieManager.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/DocumentStartUserScriptAlertCrash.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/DOMWindowExtensionBasic.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/DOMWindowExtensionNoCache.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/DownloadDecideDestinationCrash.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/EvaluateJavaScript.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/FailedLoad.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/Find.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/ForceRepaint.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/FrameMIMETypeHTML.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/FrameMIMETypePNG.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/Geolocation.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/GetInjectedBundleInitializationUserDataCallback.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/HitTestResultNodeHandle.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/InjectedBundleBasic.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/InjectedBundleFrameHitTest.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/InjectedBundleInitializationUserDataCallbackWins.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/LoadAlternateHTMLStringWithNonDirectoryURL.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/LoadCanceledNoServerRedirectCallback.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/LoadPageOnCrash.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/MouseMoveAfterCrash.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/NewFirstVisuallyNonEmptyLayout.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFails.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutForImages.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFrames.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/PageLoadBasic.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/PageLoadDidChangeLocationWithinPageForFrame.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/ParentFrame.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/PreventEmptyUserAgent.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/PrivateBrowsingPushStateNoHistoryCallback.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/ReloadPageAfterCrash.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/ResizeWindowAfterCrash.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/RestoreSessionStateContainingFormData.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/ShouldGoToBackForwardListItem.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/TextFieldDidBeginAndEndEditing.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/UserMedia.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/UserMessage.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WillSendSubmitEvent.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WKPageGetScaleFactorNotZero.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WKPreferences.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WKString.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WKStringJSString.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WKURL.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/gtk/InputMethodFilter.cpp
+)
+
+target_link_libraries(TestWebKit2 ${test_webkit2_api_LIBRARIES})
+add_test(TestWebKit2 ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebKit2/TestWebKit2)
+set_tests_properties(TestWebKit2 PROPERTIES TIMEOUT 60)
+set_target_properties(TestWebKit2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebKit2)
+
+if (ENABLE_SECCOMP_FILTERS)
+ # This test needs to be in its own executable. It's a general test of the
+ # seccomp filter mechanism, and the filters it sets are incompatible with
+ # the correct operation of WebKit and the other tests.
+ add_executable(TestSeccompFilters
+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/SeccompFilters.cpp
+ )
+
+ target_link_libraries(TestSeccompFilters ${test_webkit2_api_LIBRARIES})
+ add_test(TestSeccompFilters ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebKit2/TestWebKit2)
+ set_tests_properties(TestSeccompFilters PROPERTIES TIMEOUT 5)
+ set_target_properties(TestSeccompFilters PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebKit2)
+endif ()
+
+set(TestWebCoreGtk_SOURCES
+ ${TESTWEBKITAPI_DIR}/Tests/WebCore/gtk/UserAgentQuirks.cpp
+)
+
+add_executable(TestWebCore
+ ${test_main_SOURCES}
+ ${TestWebCoreGtk_SOURCES}
+ ${TESTWEBKITAPI_DIR}/TestsController.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebCore/LayoutUnit.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebCore/URL.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebCore/SharedBuffer.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebCore/FileSystem.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WebCore/PublicSuffix.cpp
+)
+
+target_link_libraries(TestWebCore ${test_webcore_LIBRARIES})
+add_dependencies(TestWebCore ${ForwardingHeadersForTestWebKitAPI_NAME})
+
+add_test(TestWebCore ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebCore/TestWebCore)
+set_tests_properties(TestWebCore PROPERTIES TIMEOUT 60)
+set_target_properties(TestWebCore PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebCore)
+
+list(APPEND TestWTF_SOURCES
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/glib/GMainLoopSource.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/glib/GUniquePtr.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/glib/WorkQueueGLib.cpp
+)
diff --git a/Tools/TestWebKitAPI/PlatformUtilities.cpp b/Tools/TestWebKitAPI/PlatformUtilities.cpp
new file mode 100644
index 000000000..c6e4882b7
--- /dev/null
+++ b/Tools/TestWebKitAPI/PlatformUtilities.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+
+#include <wtf/StdLibExtras.h>
+
+namespace TestWebKitAPI {
+namespace Util {
+
+WKContextRef createContextWithInjectedBundle()
+{
+ WKRetainPtr<WKStringRef> injectedBundlePath(AdoptWK, createInjectedBundlePath());
+ WKContextRef context = WKContextCreateWithInjectedBundlePath(injectedBundlePath.get());
+
+ return context;
+}
+
+WKDictionaryRef createInitializationDictionaryForInjectedBundleTest(const std::string& testName, WKTypeRef userData)
+{
+ WKMutableDictionaryRef initializationDictionary = WKMutableDictionaryCreate();
+
+ WKRetainPtr<WKStringRef> testNameKey(AdoptWK, WKStringCreateWithUTF8CString("TestName"));
+ WKRetainPtr<WKStringRef> testNameString(AdoptWK, WKStringCreateWithUTF8CString(testName.c_str()));
+ WKDictionarySetItem(initializationDictionary, testNameKey.get(), testNameString.get());
+
+ WKRetainPtr<WKStringRef> userDataKey(AdoptWK, WKStringCreateWithUTF8CString("UserData"));
+ WKDictionarySetItem(initializationDictionary, userDataKey.get(), userData);
+
+ return initializationDictionary;
+}
+
+WKContextRef createContextForInjectedBundleTest(const std::string& testName, WKTypeRef userData)
+{
+ WKContextRef context = createContextWithInjectedBundle();
+
+ WKRetainPtr<WKDictionaryRef> initializationDictionary(AdoptWK, createInitializationDictionaryForInjectedBundleTest(testName, userData));
+ WKContextSetInitializationUserDataForInjectedBundle(context, initializationDictionary.get());
+
+ return context;
+}
+
+std::string toSTD(WKStringRef string)
+{
+ size_t bufferSize = WKStringGetMaximumUTF8CStringSize(string);
+ auto buffer = std::make_unique<char[]>(bufferSize);
+ size_t stringLength = WKStringGetUTF8CString(string, buffer.get(), bufferSize);
+ return std::string(buffer.get(), stringLength - 1);
+}
+
+std::string toSTD(WKRetainPtr<WKStringRef> string)
+{
+ return toSTD(string.get());
+}
+
+std::string toSTD(const char* string)
+{
+ return std::string(string);
+}
+
+WKRetainPtr<WKStringRef> toWK(const char* utf8String)
+{
+ return WKRetainPtr<WKStringRef>(AdoptWK, WKStringCreateWithUTF8CString(utf8String));
+}
+
+} // namespace Util
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/PlatformUtilities.h b/Tools/TestWebKitAPI/PlatformUtilities.h
new file mode 100644
index 000000000..2e1e1b0b7
--- /dev/null
+++ b/Tools/TestWebKitAPI/PlatformUtilities.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PlatformUtilities_h
+#define PlatformUtilities_h
+
+#include <WebKit/WKNativeEvent.h>
+#include <WebKit/WKRetainPtr.h>
+#include <string>
+
+#if USE(FOUNDATION)
+OBJC_CLASS NSString;
+#endif
+
+namespace TestWebKitAPI {
+namespace Util {
+
+// Runs a platform runloop until the 'done' is true.
+void run(bool* done);
+void sleep(double seconds);
+
+std::string toSTD(const char*);
+#if USE(FOUNDATION)
+std::string toSTD(NSString *);
+#endif
+
+#if WK_HAVE_C_SPI
+
+WKContextRef createContextWithInjectedBundle();
+WKContextRef createContextForInjectedBundleTest(const std::string&, WKTypeRef userData = 0);
+WKDictionaryRef createInitializationDictionaryForInjectedBundleTest(const std::string&, WKTypeRef userData);
+
+WKStringRef createInjectedBundlePath();
+WKURLRef createURLForResource(const char* resource, const char* extension);
+WKURLRef URLForNonExistentResource();
+WKRetainPtr<WKStringRef> MIMETypeForWKURLResponse(WKURLResponseRef);
+
+bool isKeyDown(WKNativeEventPtr);
+
+std::string toSTD(WKStringRef);
+std::string toSTD(WKRetainPtr<WKStringRef>);
+
+WKRetainPtr<WKStringRef> toWK(const char* utf8String);
+
+#endif // WK_HAVE_C_SPI
+
+template<typename T, typename U>
+static inline ::testing::AssertionResult assertWKStringEqual(const char* expected_expression, const char* actual_expression, T expected, U actual)
+{
+ return ::testing::internal::CmpHelperSTREQ(expected_expression, actual_expression, Util::toSTD(expected).c_str(), Util::toSTD(actual).c_str());
+}
+
+#define EXPECT_WK_STREQ(expected, actual) \
+ EXPECT_PRED_FORMAT2(TestWebKitAPI::Util::assertWKStringEqual, expected, actual)
+
+} // namespace Util
+} // namespace TestWebKitAPI
+
+#endif // PlatformUtilities_h
diff --git a/Tools/TestWebKitAPI/PlatformWebView.h b/Tools/TestWebKitAPI/PlatformWebView.h
new file mode 100644
index 000000000..2e11cf7a6
--- /dev/null
+++ b/Tools/TestWebKitAPI/PlatformWebView.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PlatformWebView_h
+#define PlatformWebView_h
+
+#if USE(CG)
+#include <CoreGraphics/CGGeometry.h>
+#endif
+
+#if PLATFORM(MAC)
+#include <objc/objc.h>
+#endif
+
+#ifdef __APPLE__
+#ifdef __OBJC__
+@class WKView;
+@class NSWindow;
+#else
+class WKView;
+class NSWindow;
+#endif
+typedef WKView *PlatformWKView;
+typedef NSWindow *PlatformWindow;
+#elif PLATFORM(GTK)
+typedef WKViewRef PlatformWKView;
+typedef GtkWidget *PlatformWindow;
+#elif PLATFORM(EFL)
+typedef Evas_Object* PlatformWKView;
+typedef Ecore_Evas* PlatformWindow;
+#endif
+
+namespace TestWebKitAPI {
+
+class PlatformWebView {
+public:
+ explicit PlatformWebView(WKPageConfigurationRef);
+ explicit PlatformWebView(WKContextRef, WKPageGroupRef = 0);
+ explicit PlatformWebView(WKPageRef relatedPage);
+#if PLATFORM(MAC)
+ explicit PlatformWebView(WKContextRef, WKPageGroupRef, Class wkViewSubclass);
+#endif
+ ~PlatformWebView();
+
+ WKPageRef page() const;
+ PlatformWKView platformView() const { return m_view; }
+ void resizeTo(unsigned width, unsigned height);
+ void focus();
+
+ void simulateSpacebarKeyPress();
+ void simulateAltKeyPress();
+ void simulateRightClick(unsigned x, unsigned y);
+ void simulateMouseMove(unsigned x, unsigned y);
+#if PLATFORM(MAC)
+ void simulateButtonClick(WKEventMouseButton, unsigned x, unsigned y, WKEventModifiers);
+#endif
+
+private:
+#if PLATFORM(MAC)
+ void initialize(WKPageConfigurationRef, Class wkViewSubclass);
+#endif
+
+ PlatformWKView m_view;
+ PlatformWindow m_window;
+};
+
+} // namespace TestWebKitAPI
+
+#endif // PlatformWebView_h
diff --git a/Tools/TestWebKitAPI/Test.h b/Tools/TestWebKitAPI/Test.h
new file mode 100644
index 000000000..ca43924a2
--- /dev/null
+++ b/Tools/TestWebKitAPI/Test.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Test_h
+#define Test_h
+
+namespace TestWebKitAPI {
+
+#define EXPECT_NOT_NULL(expression) \
+ EXPECT_TRUE(expression)
+
+#define EXPECT_NULL(expression) \
+ EXPECT_TRUE(!(expression))
+
+#define ASSERT_NOT_NULL(expression) \
+ ASSERT_TRUE(expression)
+
+#define ASSERT_NULL(expression) \
+ ASSERT_TRUE(!(expression))
+
+} // namespace TestWebKitAPI
+
+#endif // Test_h
diff --git a/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp b/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp
new file mode 100644
index 000000000..e23943143
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/text/AtomicString.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF, AtomicStringCreationFromLiteral)
+{
+ AtomicString stringWithTemplate("Template Literal", AtomicString::ConstructFromLiteral);
+ ASSERT_EQ(strlen("Template Literal"), stringWithTemplate.length());
+ ASSERT_TRUE(stringWithTemplate == "Template Literal");
+ ASSERT_TRUE(stringWithTemplate.string().is8Bit());
+
+ const char* programmaticStringData = "Explicit Size Literal";
+ AtomicString programmaticString(programmaticStringData, strlen(programmaticStringData), AtomicString::ConstructFromLiteral);
+ ASSERT_EQ(strlen(programmaticStringData), programmaticString.length());
+ ASSERT_TRUE(programmaticString.string().is8Bit());
+ ASSERT_EQ(programmaticStringData, reinterpret_cast<const char*>(programmaticString.string().characters8()));
+}
+
+TEST(WTF, AtomicStringCreationFromLiteralUniqueness)
+{
+ AtomicString string1("Template Literal", AtomicString::ConstructFromLiteral);
+ AtomicString string2("Template Literal", AtomicString::ConstructFromLiteral);
+ ASSERT_EQ(string1.impl(), string2.impl());
+
+ AtomicString string3("Template Literal");
+ ASSERT_EQ(string1.impl(), string3.impl());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/BloomFilter.cpp b/Tools/TestWebKitAPI/Tests/WTF/BloomFilter.cpp
new file mode 100644
index 000000000..802165211
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/BloomFilter.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/BloomFilter.h>
+#include <wtf/RandomNumber.h>
+#include <wtf/SHA1.h>
+
+namespace TestWebKitAPI {
+
+static Vector<unsigned> generateRandomHashes(size_t hashCount)
+{
+ Vector<unsigned> hashes;
+ for (unsigned i = 0; i < hashCount; ++i)
+ hashes.append(static_cast<unsigned>(randomNumber() * std::numeric_limits<unsigned>::max()));
+ return hashes;
+}
+
+static Vector<SHA1::Digest> generateRandomDigests(size_t hashCount)
+{
+ Vector<SHA1::Digest> hashes;
+ SHA1 sha1;
+ for (unsigned i = 0; i < hashCount; ++i) {
+ double random = randomNumber();
+ sha1.addBytes(reinterpret_cast<uint8_t*>(&random), sizeof(double));
+ SHA1::Digest digest;
+ sha1.computeHash(digest);
+ hashes.append(digest);
+ }
+ return hashes;
+}
+
+TEST(WTF_BloomFilter, Basic)
+{
+ const unsigned hashCount = 1000;
+ auto hashes = generateRandomHashes(hashCount);
+
+ BloomFilter<16> filter;
+ for (auto hash : hashes)
+ filter.add(hash);
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+
+ auto moreHashes = generateRandomHashes(hashCount);
+ unsigned mayContainCount = 0;
+ for (auto hash : moreHashes)
+ mayContainCount += filter.mayContain(hash) ? 1 : 0;
+ // False positive rate is ~0.09% so this should always be true.
+ EXPECT_TRUE(mayContainCount < hashCount / 10);
+
+ for (auto hash : moreHashes)
+ filter.add(hash);
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+ for (auto hash : moreHashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+}
+
+TEST(WTF_BloomFilter, BasicDigest)
+{
+ const unsigned hashCount = 1000;
+ auto hashes = generateRandomDigests(hashCount);
+
+ BloomFilter<20> filter;
+ for (auto hash : hashes)
+ filter.add(hash);
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+
+ auto moreHashes = generateRandomDigests(hashCount);
+ unsigned mayContainCount = 0;
+ for (auto hash : moreHashes)
+ mayContainCount += filter.mayContain(hash) ? 1 : 0;
+ // False positive rate is ~0.000004% so this should always be true.
+ EXPECT_TRUE(mayContainCount < hashCount / 10);
+
+ for (auto hash : moreHashes)
+ filter.add(hash);
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+ for (auto hash : moreHashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+}
+
+TEST(WTF_BloomFilter, BasicCounting)
+{
+ const unsigned hashCount = 1000;
+ auto hashes = generateRandomHashes(hashCount);
+
+ CountingBloomFilter<16> filter;
+ for (auto hash : hashes)
+ filter.add(hash);
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+
+ for (auto hash : hashes)
+ filter.add(hash);
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+
+ for (auto hash : hashes)
+ filter.remove(hash);
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+
+ auto moreHashes = generateRandomHashes(hashCount);
+ unsigned mayContainCount = 0;
+ for (auto hash : moreHashes)
+ mayContainCount += filter.mayContain(hash) ? 1 : 0;
+ // False positive rate is ~0.09% so this should always be true.
+ EXPECT_TRUE(mayContainCount < hashCount / 10);
+
+ for (auto hash : moreHashes)
+ filter.add(hash);
+ for (auto hash : hashes)
+ filter.remove(hash);
+
+ for (auto hash : moreHashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+
+ for (auto hash : moreHashes)
+ filter.remove(hash);
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(!filter.mayContain(hash));
+ for (auto hash : moreHashes)
+ EXPECT_TRUE(!filter.mayContain(hash));
+}
+
+TEST(WTF_BloomFilter, Clear)
+{
+ const unsigned hashCount = 1000;
+ auto hashes = generateRandomHashes(hashCount);
+
+ BloomFilter<16> filter;
+ for (auto hash : hashes)
+ filter.add(hash);
+
+ filter.clear();
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(!filter.mayContain(hash));
+}
+
+TEST(WTF_BloomFilter, ClearCounting)
+{
+ const unsigned hashCount = 1000;
+ auto hashes = generateRandomHashes(hashCount);
+
+ CountingBloomFilter<16> filter;
+ for (auto hash : hashes)
+ filter.add(hash);
+ for (auto hash : hashes)
+ filter.add(hash);
+
+ filter.clear();
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(!filter.mayContain(hash));
+}
+
+TEST(WTF_BloomFilter, CountingOverflow)
+{
+ const unsigned hashCount = 1000;
+ auto hashes = generateRandomHashes(hashCount);
+
+ CountingBloomFilter<16> filter;
+ for (auto hash : hashes)
+ filter.add(hash);
+
+ for (unsigned i = 0; i < filter.maximumCount() + 100; ++i)
+ filter.add(hashes[0]);
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+
+ for (auto hash : hashes)
+ filter.remove(hash);
+
+ unsigned mayContainCount = 0;
+ for (auto hash : hashes) {
+ if (hash == hashes[0])
+ EXPECT_TRUE(filter.mayContain(hash));
+ else
+ mayContainCount += filter.mayContain(hash) ? 1 : 0;
+ }
+ // False positive rate should be very low.
+ EXPECT_TRUE(mayContainCount < hashCount / 100);
+
+ for (unsigned i = 0; i < filter.maximumCount() + 100; ++i)
+ filter.remove(hashes[0]);
+
+ // The bucket has overflowed and is stuck.
+ EXPECT_TRUE(filter.mayContain(hashes[0]));
+}
+
+TEST(WTF_BloomFilter, Combine)
+{
+ const unsigned hashCount = 1000;
+ auto hashes = generateRandomHashes(hashCount);
+
+ BloomFilter<16> filter;
+ for (auto hash : hashes)
+ filter.add(hash);
+
+ auto moreHashes = generateRandomHashes(hashCount);
+
+ BloomFilter<16> anotherFilter;
+ for (auto hash : moreHashes)
+ anotherFilter.add(hash);
+
+ filter.add(anotherFilter);
+
+ for (auto hash : hashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+ for (auto hash : moreHashes)
+ EXPECT_TRUE(filter.mayContain(hash));
+}
+
+}
diff --git a/Tools/TestWebKitAPI/Tests/WTF/CString.cpp b/Tools/TestWebKitAPI/Tests/WTF/CString.cpp
new file mode 100644
index 000000000..735d7dd8b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/CString.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/text/CString.h>
+
+TEST(WTF, CStringNullStringConstructor)
+{
+ CString string;
+ ASSERT_TRUE(string.isNull());
+ ASSERT_EQ(string.data(), static_cast<const char*>(0));
+ ASSERT_EQ(string.length(), static_cast<size_t>(0));
+
+ CString stringFromCharPointer(static_cast<const char*>(0));
+ ASSERT_TRUE(stringFromCharPointer.isNull());
+ ASSERT_EQ(stringFromCharPointer.data(), static_cast<const char*>(0));
+ ASSERT_EQ(stringFromCharPointer.length(), static_cast<size_t>(0));
+
+ CString stringFromCharAndLength(static_cast<const char*>(0), 0);
+ ASSERT_TRUE(stringFromCharAndLength.isNull());
+ ASSERT_EQ(stringFromCharAndLength.data(), static_cast<const char*>(0));
+ ASSERT_EQ(stringFromCharAndLength.length(), static_cast<size_t>(0));
+}
+
+TEST(WTF, CStringEmptyEmptyConstructor)
+{
+ const char* emptyString = "";
+ CString string(emptyString);
+ ASSERT_FALSE(string.isNull());
+ ASSERT_EQ(string.length(), static_cast<size_t>(0));
+ ASSERT_EQ(string.data()[0], 0);
+
+ CString stringWithLength(emptyString, 0);
+ ASSERT_FALSE(stringWithLength.isNull());
+ ASSERT_EQ(stringWithLength.length(), static_cast<size_t>(0));
+ ASSERT_EQ(stringWithLength.data()[0], 0);
+}
+
+TEST(WTF, CStringEmptyRegularConstructor)
+{
+ const char* referenceString = "WebKit";
+
+ CString string(referenceString);
+ ASSERT_FALSE(string.isNull());
+ ASSERT_EQ(string.length(), strlen(referenceString));
+ ASSERT_STREQ(referenceString, string.data());
+
+ CString stringWithLength(referenceString, 6);
+ ASSERT_FALSE(stringWithLength.isNull());
+ ASSERT_EQ(stringWithLength.length(), strlen(referenceString));
+ ASSERT_STREQ(referenceString, stringWithLength.data());
+}
+
+TEST(WTF, CStringUninitializedConstructor)
+{
+ char* buffer;
+ CString emptyString = CString::newUninitialized(0, buffer);
+ ASSERT_FALSE(emptyString.isNull());
+ ASSERT_EQ(buffer, emptyString.data());
+ ASSERT_EQ(buffer[0], 0);
+
+ const size_t length = 25;
+ CString uninitializedString = CString::newUninitialized(length, buffer);
+ ASSERT_FALSE(uninitializedString.isNull());
+ ASSERT_EQ(buffer, uninitializedString.data());
+ ASSERT_EQ(uninitializedString.data()[length], 0);
+}
+
+TEST(WTF, CStringZeroTerminated)
+{
+ const char* referenceString = "WebKit";
+ CString stringWithLength(referenceString, 3);
+ ASSERT_EQ(stringWithLength.data()[3], 0);
+}
+
+TEST(WTF, CStringCopyOnWrite)
+{
+ const char* initialString = "Webkit";
+ CString string(initialString);
+ CString copy = string;
+
+ string.mutableData()[3] = 'K';
+ ASSERT_TRUE(string != copy);
+ ASSERT_STREQ(string.data(), "WebKit");
+ ASSERT_STREQ(copy.data(), initialString);
+}
+
+TEST(WTF, CStringComparison)
+{
+ // Comparison with another CString.
+ CString a;
+ CString b;
+ ASSERT_TRUE(a == b);
+ ASSERT_FALSE(a != b);
+ a = "a";
+ b = CString();
+ ASSERT_FALSE(a == b);
+ ASSERT_TRUE(a != b);
+ a = "a";
+ b = "b";
+ ASSERT_FALSE(a == b);
+ ASSERT_TRUE(a != b);
+ a = "a";
+ b = "a";
+ ASSERT_TRUE(a == b);
+ ASSERT_FALSE(a != b);
+ a = "a";
+ b = "aa";
+ ASSERT_FALSE(a == b);
+ ASSERT_TRUE(a != b);
+ a = "";
+ b = "";
+ ASSERT_TRUE(a == b);
+ ASSERT_FALSE(a != b);
+ a = "";
+ b = CString();
+ ASSERT_FALSE(a == b);
+ ASSERT_TRUE(a != b);
+ a = "a";
+ b = "";
+ ASSERT_FALSE(a == b);
+ ASSERT_TRUE(a != b);
+
+ // Comparison with a const char*.
+ CString c;
+ const char* d = 0;
+ ASSERT_TRUE(c == d);
+ ASSERT_FALSE(c != d);
+ c = "c";
+ d = 0;
+ ASSERT_FALSE(c == d);
+ ASSERT_TRUE(c != d);
+ c = CString();
+ d = "d";
+ ASSERT_FALSE(c == d);
+ ASSERT_TRUE(c != d);
+ c = "c";
+ d = "d";
+ ASSERT_FALSE(c == d);
+ ASSERT_TRUE(c != d);
+ c = "c";
+ d = "c";
+ ASSERT_TRUE(c == d);
+ ASSERT_FALSE(c != d);
+ c = "c";
+ d = "cc";
+ ASSERT_FALSE(c == d);
+ ASSERT_TRUE(c != d);
+ c = "cc";
+ d = "c";
+ ASSERT_FALSE(c == d);
+ ASSERT_TRUE(c != d);
+ c = "";
+ d = "";
+ ASSERT_TRUE(c == d);
+ ASSERT_FALSE(c != d);
+ c = "";
+ d = 0;
+ ASSERT_FALSE(c == d);
+ ASSERT_TRUE(c != d);
+ c = CString();
+ d = "";
+ ASSERT_FALSE(c == d);
+ ASSERT_TRUE(c != d);
+ c = "a";
+ d = "";
+ ASSERT_FALSE(c == d);
+ ASSERT_TRUE(c != d);
+ c = "";
+ d = "b";
+ ASSERT_FALSE(c == d);
+ ASSERT_TRUE(c != d);
+}
diff --git a/Tools/TestWebKitAPI/Tests/WTF/CheckedArithmeticOperations.cpp b/Tools/TestWebKitAPI/Tests/WTF/CheckedArithmeticOperations.cpp
new file mode 100644
index 000000000..d6b548316
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/CheckedArithmeticOperations.cpp
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2011, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <wtf/CheckedArithmetic.h>
+
+namespace TestWebKitAPI {
+
+class OverflowCrashLogger {
+protected:
+ void overflowed()
+ {
+ m_overflowCount++;
+ }
+
+ void clearOverflow()
+ {
+ m_overflowCount = 0;
+ }
+
+ static void crash()
+ {
+ s_didCrash = true;
+ }
+
+public:
+ void reset()
+ {
+ m_overflowCount = 0;
+ s_didCrash = false;
+ }
+
+ bool hasOverflowed() const { return m_overflowCount > 0; }
+ int overflowCount() const { return m_overflowCount; }
+
+ bool didCrash() const { return s_didCrash; }
+
+private:
+ int m_overflowCount { 0 };
+ static bool s_didCrash;
+};
+
+bool OverflowCrashLogger::s_didCrash = false;
+
+template <typename type>
+static void resetOverflow(Checked<type, OverflowCrashLogger>& value)
+{
+ value.reset();
+ value = 100;
+ value *= std::numeric_limits<type>::max();
+}
+
+#define CheckedArithmeticTest(type, Coercer, MixedSignednessTester) \
+ TEST(WTF, Checked_##type) \
+ { \
+ typedef Coercer<type> CoercerType; \
+ typedef MixedSignednessTester<type, CoercerType> MixedSignednessTesterType; \
+ CheckedArithmeticTester<type, CoercerType, MixedSignednessTesterType>::run(); \
+ }
+
+#define coerceLiteral(x) Coercer::coerce(x)
+
+template <typename type, typename Coercer, typename MixedSignednessTester>
+class CheckedArithmeticTester {
+public:
+ static void run()
+ {
+ Checked<type, RecordOverflow> value;
+ EXPECT_EQ(coerceLiteral(0), value.unsafeGet());
+ EXPECT_EQ(std::numeric_limits<type>::max(), (value + std::numeric_limits<type>::max()).unsafeGet());
+ EXPECT_EQ(std::numeric_limits<type>::max(), (std::numeric_limits<type>::max() + value).unsafeGet());
+ EXPECT_EQ(std::numeric_limits<type>::min(), (value + std::numeric_limits<type>::min()).unsafeGet());
+ EXPECT_EQ(std::numeric_limits<type>::min(), (std::numeric_limits<type>::min() + value).unsafeGet());
+
+ EXPECT_EQ(coerceLiteral(0), (value * coerceLiteral(0)).unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), (coerceLiteral(0) * value).unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), (value * value).unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), (value - coerceLiteral(0)).unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), (coerceLiteral(0) - value).unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), (value - value).unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), (value++).unsafeGet());
+ EXPECT_EQ(coerceLiteral(1), (value--).unsafeGet());
+ EXPECT_EQ(coerceLiteral(1), (++value).unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), (--value).unsafeGet());
+ EXPECT_EQ(coerceLiteral(10), (value += coerceLiteral(10)).unsafeGet());
+ EXPECT_EQ(coerceLiteral(10), value.unsafeGet());
+ EXPECT_EQ(coerceLiteral(100), (value *= coerceLiteral(10)).unsafeGet());
+ EXPECT_EQ(coerceLiteral(100), value.unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), (value -= coerceLiteral(100)).unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), value.unsafeGet());
+ value = 10;
+ EXPECT_EQ(coerceLiteral(10), value.unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), (value - coerceLiteral(10)).unsafeGet());
+ EXPECT_EQ(coerceLiteral(10), value.unsafeGet());
+
+ value = std::numeric_limits<type>::min();
+ EXPECT_EQ(true, (Checked<type, RecordOverflow>(value - coerceLiteral(1))).hasOverflowed());
+ EXPECT_EQ(true, !((value--).hasOverflowed()));
+ EXPECT_EQ(true, value.hasOverflowed());
+ value = std::numeric_limits<type>::max();
+ EXPECT_EQ(true, !value.hasOverflowed());
+ EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + coerceLiteral(1))).hasOverflowed());
+ EXPECT_EQ(true, !(value++).hasOverflowed());
+ EXPECT_EQ(true, value.hasOverflowed());
+ value = std::numeric_limits<type>::max();
+ EXPECT_EQ(true, (value += coerceLiteral(1)).hasOverflowed());
+ EXPECT_EQ(true, value.hasOverflowed());
+
+ value = 10;
+ type _value = 0;
+ EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked<type, RecordOverflow>(0)).safeGet(_value));
+ _value = 0;
+ EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked<type, RecordOverflow>(0) * value).safeGet(_value));
+ _value = 0;
+ EXPECT_EQ(true, CheckedState::DidOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value));
+ _value = 0;
+ EXPECT_EQ(true, CheckedState::DidOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * value).safeGet(_value));
+ value = 0;
+ _value = 0;
+ EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value));
+ _value = 0;
+ EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * value).safeGet(_value));
+ value = 1;
+ _value = 0;
+ EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value));
+ _value = 0;
+ EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * value).safeGet(_value));
+ _value = 0;
+ value = 0;
+ EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value));
+ _value = 0;
+ EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * (type)0).safeGet(_value));
+ _value = 0;
+ value = 1;
+ EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value));
+ _value = 0;
+ EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * (type)1).safeGet(_value));
+ _value = 0;
+ value = 2;
+ EXPECT_EQ(true, CheckedState::DidOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value));
+ _value = 0;
+ EXPECT_EQ(true, CheckedState::DidOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * (type)2).safeGet(_value));
+ value = 10;
+ EXPECT_EQ(true, (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).hasOverflowed());
+
+
+ Checked<type, OverflowCrashLogger> nvalue; // to hold a not overflowed value.
+ Checked<type, OverflowCrashLogger> ovalue; // to hold an overflowed value.
+ bool unused;
+
+ _value = 75;
+ type _largeValue = 100;
+ type _smallValue = 50;
+
+ value = _smallValue;
+ nvalue = _value;
+ ovalue = _value;
+
+ // Make sure the OverflowCrashLogger is working as expected.
+ EXPECT_EQ(false, (ovalue.hasOverflowed()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), ovalue.hasOverflowed()));
+ EXPECT_EQ(false, (resetOverflow(ovalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (unused = (ovalue == ovalue), ovalue.didCrash()));
+ EXPECT_EQ(false, (resetOverflow(ovalue), ovalue.didCrash()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+ EXPECT_EQ(false, nvalue.didCrash());
+
+ // Test operator== that should not overflow nor crash.
+ EXPECT_EQ(true, (nvalue == nvalue));
+ EXPECT_EQ(true, (nvalue == Checked<type, OverflowCrashLogger>(_value)));
+ EXPECT_EQ(false, (nvalue == value));
+ EXPECT_EQ(true, (nvalue == _value));
+ EXPECT_EQ(false, (nvalue == Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())));
+ EXPECT_EQ(false, (nvalue == std::numeric_limits<type>::max()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+ EXPECT_EQ(false, nvalue.didCrash());
+
+ // Test operator!= that should not overflow nor crash.
+ EXPECT_EQ(false, (nvalue != nvalue));
+ EXPECT_EQ(false, (nvalue != Checked<type, OverflowCrashLogger>(_value)));
+ EXPECT_EQ(true, (nvalue != value));
+ EXPECT_EQ(false, (nvalue != _value));
+ EXPECT_EQ(true, (nvalue != Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())));
+ EXPECT_EQ(true, (nvalue != std::numeric_limits<type>::max()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+ EXPECT_EQ(false, nvalue.didCrash());
+
+ // Test operator< that should not overflow nor crash.
+ EXPECT_EQ(false, (nvalue < nvalue));
+ EXPECT_EQ(false, (nvalue < value));
+ EXPECT_EQ(true, (nvalue < Checked<type, OverflowCrashLogger>(_largeValue)));
+ EXPECT_EQ(false, (nvalue < Checked<type, OverflowCrashLogger>(_value)));
+ EXPECT_EQ(false, (nvalue < Checked<type, OverflowCrashLogger>(_smallValue)));
+ EXPECT_EQ(true, (nvalue < _largeValue));
+ EXPECT_EQ(false, (nvalue < _value));
+ EXPECT_EQ(false, (nvalue < _smallValue));
+ EXPECT_EQ(true, (nvalue < Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())));
+ EXPECT_EQ(true, (nvalue < std::numeric_limits<type>::max()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+ EXPECT_EQ(false, nvalue.didCrash());
+
+ // Test operator<= that should not overflow nor crash.
+ EXPECT_EQ(true, (nvalue <= nvalue));
+ EXPECT_EQ(false, (nvalue <= value));
+ EXPECT_EQ(true, (nvalue <= Checked<type, OverflowCrashLogger>(_largeValue)));
+ EXPECT_EQ(true, (nvalue <= Checked<type, OverflowCrashLogger>(_value)));
+ EXPECT_EQ(false, (nvalue <= Checked<type, OverflowCrashLogger>(_smallValue)));
+ EXPECT_EQ(true, (nvalue <= _largeValue));
+ EXPECT_EQ(true, (nvalue <= _value));
+ EXPECT_EQ(false, (nvalue <= _smallValue));
+ EXPECT_EQ(true, (nvalue <= Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())));
+ EXPECT_EQ(true, (nvalue <= std::numeric_limits<type>::max()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+ EXPECT_EQ(false, nvalue.didCrash());
+
+ // Test operator> that should not overflow nor crash.
+ EXPECT_EQ(false, (nvalue > nvalue));
+ EXPECT_EQ(true, (nvalue > value));
+ EXPECT_EQ(false, (nvalue > Checked<type, OverflowCrashLogger>(_largeValue)));
+ EXPECT_EQ(false, (nvalue > Checked<type, OverflowCrashLogger>(_value)));
+ EXPECT_EQ(true, (nvalue > Checked<type, OverflowCrashLogger>(_smallValue)));
+ EXPECT_EQ(false, (nvalue > _largeValue));
+ EXPECT_EQ(false, (nvalue > _value));
+ EXPECT_EQ(true, (nvalue > _smallValue));
+ EXPECT_EQ(false, (nvalue > Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())));
+ EXPECT_EQ(false, (nvalue > std::numeric_limits<type>::max()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+ EXPECT_EQ(false, nvalue.didCrash());
+
+ // Test operator>= that should not overflow nor crash.
+ EXPECT_EQ(true, (nvalue >= nvalue));
+ EXPECT_EQ(true, (nvalue >= value));
+ EXPECT_EQ(false, (nvalue >= Checked<type, OverflowCrashLogger>(_largeValue)));
+ EXPECT_EQ(true, (nvalue >= Checked<type, OverflowCrashLogger>(_value)));
+ EXPECT_EQ(true, (nvalue >= Checked<type, OverflowCrashLogger>(_smallValue)));
+ EXPECT_EQ(false, (nvalue >= _largeValue));
+ EXPECT_EQ(true, (nvalue >= _value));
+ EXPECT_EQ(true, (nvalue >= _smallValue));
+ EXPECT_EQ(false, (nvalue >= Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())));
+ EXPECT_EQ(false, (nvalue >= std::numeric_limits<type>::max()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+ EXPECT_EQ(false, nvalue.didCrash());
+
+ // Test operator== with an overflowed value.
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == ovalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == _value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == _value * std::numeric_limits<type>::max()), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == std::numeric_limits<type>::max()), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == nvalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue == ovalue), ovalue.didCrash()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+
+ // Test operator!= with an overflowed value.
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != ovalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != _value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != _value * std::numeric_limits<type>::max()), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != std::numeric_limits<type>::max()), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != nvalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue != ovalue), ovalue.didCrash()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+
+ // Test operator< with an overflowed value.
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < ovalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < Checked<type, OverflowCrashLogger>(_largeValue)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < Checked<type, OverflowCrashLogger>(_smallValue)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < _largeValue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < _value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < _smallValue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < std::numeric_limits<type>::max()), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < nvalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue < ovalue), ovalue.didCrash()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+
+ // Test operator<= with an overflowed value.
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= ovalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= Checked<type, OverflowCrashLogger>(_largeValue)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= Checked<type, OverflowCrashLogger>(_smallValue)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= _largeValue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= _value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= _smallValue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= std::numeric_limits<type>::max()), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= nvalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue <= ovalue), ovalue.didCrash()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+
+ // Test operator> with an overflowed value.
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > ovalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > Checked<type, OverflowCrashLogger>(_largeValue)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > Checked<type, OverflowCrashLogger>(_smallValue)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > _largeValue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > _value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > _smallValue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > std::numeric_limits<type>::max()), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > nvalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue > ovalue), ovalue.didCrash()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+
+ // Test operator>= with an overflowed value.
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= ovalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= Checked<type, OverflowCrashLogger>(_largeValue)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= Checked<type, OverflowCrashLogger>(_smallValue)), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= _largeValue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= _value), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= _smallValue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= std::numeric_limits<type>::max()), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= nvalue), ovalue.didCrash()));
+ EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue >= ovalue), ovalue.didCrash()));
+
+ EXPECT_EQ(false, nvalue.hasOverflowed());
+
+ MixedSignednessTester::run();
+ }
+};
+
+template <typename type, typename Coercer>
+class AllowMixedSignednessTest {
+public:
+ static void run()
+ {
+ Checked<type, RecordOverflow> value;
+ value = 10;
+
+ EXPECT_EQ(coerceLiteral(0), (value + -10).unsafeGet());
+ EXPECT_EQ(0U, (value - 10U).unsafeGet());
+ EXPECT_EQ(coerceLiteral(0), (-10 + value).unsafeGet());
+ EXPECT_EQ(0U, (10U - value).unsafeGet());
+ value = std::numeric_limits<type>::min();
+ EXPECT_EQ(true, (Checked<type, RecordOverflow>(value - 1)).hasOverflowed());
+ EXPECT_EQ(true, !(value--).hasOverflowed());
+ EXPECT_EQ(true, value.hasOverflowed());
+ value = std::numeric_limits<type>::max();
+ EXPECT_EQ(true, !value.hasOverflowed());
+ EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + 1)).hasOverflowed());
+ EXPECT_EQ(true, !(value++).hasOverflowed());
+ EXPECT_EQ(true, value.hasOverflowed());
+ value = std::numeric_limits<type>::max();
+ EXPECT_EQ(true, (value += 1).hasOverflowed());
+ EXPECT_EQ(true, value.hasOverflowed());
+ value = std::numeric_limits<type>::min();
+ EXPECT_EQ(true, (value - 1U).hasOverflowed());
+ EXPECT_EQ(true, !(value--).hasOverflowed());
+ EXPECT_EQ(true, value.hasOverflowed());
+ value = std::numeric_limits<type>::max();
+ EXPECT_EQ(true, !value.hasOverflowed());
+ EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + 1U)).hasOverflowed());
+ EXPECT_EQ(true, !(value++).hasOverflowed());
+ EXPECT_EQ(true, value.hasOverflowed());
+ value = std::numeric_limits<type>::max();
+ EXPECT_EQ(true, (value += 1U).hasOverflowed());
+ EXPECT_EQ(true, value.hasOverflowed());
+ }
+};
+
+template <typename type, typename Coercer>
+class IgnoreMixedSignednessTest {
+public:
+ static void run() { }
+};
+
+template <typename type> class CoerceLiteralToUnsigned {
+public:
+ static unsigned coerce(type x) { return static_cast<unsigned>(x); }
+};
+
+template <typename type> class CoerceLiteralNop {
+public:
+ static type coerce(type x) { return x; }
+};
+
+CheckedArithmeticTest(int8_t, CoerceLiteralNop, IgnoreMixedSignednessTest)
+CheckedArithmeticTest(int16_t, CoerceLiteralNop, IgnoreMixedSignednessTest)
+CheckedArithmeticTest(int32_t, CoerceLiteralNop, AllowMixedSignednessTest)
+CheckedArithmeticTest(uint32_t, CoerceLiteralToUnsigned, AllowMixedSignednessTest)
+CheckedArithmeticTest(int64_t, CoerceLiteralNop, IgnoreMixedSignednessTest)
+CheckedArithmeticTest(uint64_t, CoerceLiteralToUnsigned, IgnoreMixedSignednessTest)
+
+TEST(CheckedArithmeticTest, IsInBounds)
+{
+ // bigger precision, signed, signed
+ EXPECT_TRUE(WTF::isInBounds<int32_t>(std::numeric_limits<int16_t>::max()));
+ EXPECT_TRUE(WTF::isInBounds<int32_t>(std::numeric_limits<int16_t>::min()));
+
+ // bigger precision, unsigned, signed
+ EXPECT_TRUE(WTF::isInBounds<uint32_t>(std::numeric_limits<int32_t>::max()));
+ EXPECT_FALSE(WTF::isInBounds<uint32_t>(std::numeric_limits<int16_t>::min()));
+
+ EXPECT_FALSE(WTF::isInBounds<uint32_t>((int32_t)-1));
+ EXPECT_FALSE(WTF::isInBounds<uint16_t>((int32_t)-1));
+ EXPECT_FALSE(WTF::isInBounds<unsigned long>((int)-1));
+
+ EXPECT_TRUE(WTF::isInBounds<uint32_t>((int32_t)1));
+ EXPECT_TRUE(WTF::isInBounds<uint32_t>((int16_t)1));
+ EXPECT_TRUE(WTF::isInBounds<unsigned>((int)1));
+
+ EXPECT_TRUE(WTF::isInBounds<uint32_t>((int32_t)0));
+ EXPECT_TRUE(WTF::isInBounds<uint16_t>((int32_t)0));
+ EXPECT_TRUE(WTF::isInBounds<uint32_t>((int16_t)0));
+ EXPECT_TRUE(WTF::isInBounds<unsigned>((int)0));
+
+ EXPECT_TRUE(WTF::isInBounds<uint32_t>(std::numeric_limits<int32_t>::max()));
+ EXPECT_TRUE(WTF::isInBounds<uint32_t>(std::numeric_limits<int16_t>::max()));
+ EXPECT_TRUE(WTF::isInBounds<unsigned>(std::numeric_limits<int>::max()));
+
+ // bigger precision, signed, unsigned
+ EXPECT_TRUE(WTF::isInBounds<int32_t>(std::numeric_limits<uint16_t>::max()));
+ EXPECT_FALSE(WTF::isInBounds<int32_t>(std::numeric_limits<uint32_t>::max()));
+ EXPECT_TRUE(WTF::isInBounds<int32_t>((uint32_t)0));
+
+ // bigger precision, unsigned, unsigned
+ EXPECT_TRUE(WTF::isInBounds<uint32_t>(std::numeric_limits<uint16_t>::max()));
+ EXPECT_TRUE(WTF::isInBounds<uint32_t>(std::numeric_limits<uint16_t>::min()));
+
+ // lower precision, signed signed
+ EXPECT_FALSE(WTF::isInBounds<int16_t>(std::numeric_limits<int32_t>::max()));
+ EXPECT_FALSE(WTF::isInBounds<int16_t>(std::numeric_limits<int32_t>::min()));
+ EXPECT_TRUE(WTF::isInBounds<int16_t>((int32_t)-1));
+ EXPECT_TRUE(WTF::isInBounds<int16_t>((int32_t)0));
+ EXPECT_TRUE(WTF::isInBounds<int16_t>((int32_t)1));
+ // lower precision, unsigned, signed
+ EXPECT_FALSE(WTF::isInBounds<uint16_t>(std::numeric_limits<int32_t>::max()));
+ EXPECT_FALSE(WTF::isInBounds<uint16_t>(std::numeric_limits<int32_t>::min()));
+ EXPECT_FALSE(WTF::isInBounds<uint16_t>((int32_t)-1));
+ EXPECT_TRUE(WTF::isInBounds<uint16_t>((int32_t)0));
+ EXPECT_TRUE(WTF::isInBounds<uint16_t>((int32_t)1));
+ // lower precision, signed, unsigned
+ EXPECT_FALSE(WTF::isInBounds<int16_t>(std::numeric_limits<uint32_t>::max()));
+ EXPECT_TRUE(WTF::isInBounds<int16_t>((uint32_t)0));
+ EXPECT_TRUE(WTF::isInBounds<int16_t>((uint32_t)1));
+ // lower precision, unsigned, unsigned
+ EXPECT_FALSE(WTF::isInBounds<uint16_t>(std::numeric_limits<uint32_t>::max()));
+ EXPECT_TRUE(WTF::isInBounds<uint16_t>((uint32_t)0));
+ EXPECT_TRUE(WTF::isInBounds<uint16_t>((uint32_t)1));
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/Condition.cpp b/Tools/TestWebKitAPI/Tests/WTF/Condition.cpp
new file mode 100644
index 000000000..c450d8953
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/Condition.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <mutex>
+#include <thread>
+#include <wtf/Condition.h>
+#include <wtf/DataLog.h>
+#include <wtf/Deque.h>
+#include <wtf/Lock.h>
+#include <wtf/StringPrintStream.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+
+using namespace WTF;
+
+namespace TestWebKitAPI {
+
+namespace {
+
+const bool verbose = false;
+
+enum NotifyStyle {
+ AlwaysNotifyOne,
+ TacticallyNotifyAll
+};
+
+template<typename Functor>
+void wait(Condition& condition, std::unique_lock<Lock>& locker, const Functor& predicate, std::chrono::microseconds timeout)
+{
+ if (timeout == std::chrono::microseconds::max())
+ condition.wait(locker, predicate);
+ else {
+ // This tests timeouts in the sense that it verifies that we can call wait() again after a
+ // timeout happened. That's a non-trivial piece of functionality since upon timeout the
+ // ParkingLot has to remove us from the queue.
+ while (!predicate())
+ condition.waitFor(locker, timeout, predicate);
+ }
+}
+
+void notify(NotifyStyle notifyStyle, Condition& condition, bool shouldNotify)
+{
+ switch (notifyStyle) {
+ case AlwaysNotifyOne:
+ condition.notifyOne();
+ break;
+ case TacticallyNotifyAll:
+ if (shouldNotify)
+ condition.notifyAll();
+ break;
+ }
+}
+
+void runTest(
+ unsigned numProducers,
+ unsigned numConsumers,
+ unsigned maxQueueSize,
+ unsigned numMessagesPerProducer,
+ NotifyStyle notifyStyle,
+ std::chrono::microseconds timeout = std::chrono::microseconds::max(),
+ std::chrono::microseconds delay = std::chrono::microseconds::zero())
+{
+ Deque<unsigned> queue;
+ bool shouldContinue = true;
+ Lock lock;
+ Condition emptyCondition;
+ Condition fullCondition;
+
+ Vector<ThreadIdentifier> consumerThreads;
+ Vector<ThreadIdentifier> producerThreads;
+
+ Vector<unsigned> received;
+ Lock receivedLock;
+
+ for (unsigned i = numConsumers; i--;) {
+ ThreadIdentifier threadIdentifier = createThread(
+ "Consumer thread",
+ [&] () {
+ for (;;) {
+ unsigned result;
+ unsigned shouldNotify = false;
+ {
+ std::unique_lock<Lock> locker(lock);
+ wait(
+ emptyCondition, locker,
+ [&] () {
+ if (verbose)
+ dataLog(toString(currentThread(), ": Checking consumption predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n"));
+ return !shouldContinue || !queue.isEmpty();
+ },
+ timeout);
+ if (!shouldContinue && queue.isEmpty())
+ return;
+ shouldNotify = queue.size() == maxQueueSize;
+ result = queue.takeFirst();
+ }
+ notify(notifyStyle, fullCondition, shouldNotify);
+
+ {
+ std::lock_guard<Lock> locker(receivedLock);
+ received.append(result);
+ }
+ }
+ });
+ consumerThreads.append(threadIdentifier);
+ }
+
+ std::this_thread::sleep_for(delay);
+
+ for (unsigned i = numProducers; i--;) {
+ ThreadIdentifier threadIdentifier = createThread(
+ "Producer Thread",
+ [&] () {
+ for (unsigned i = 0; i < numMessagesPerProducer; ++i) {
+ bool shouldNotify = false;
+ {
+ std::unique_lock<Lock> locker(lock);
+ wait(
+ fullCondition, locker,
+ [&] () {
+ if (verbose)
+ dataLog(toString(currentThread(), ": Checking production predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n"));
+ return queue.size() < maxQueueSize;
+ },
+ timeout);
+ shouldNotify = queue.isEmpty();
+ queue.append(i);
+ }
+ notify(notifyStyle, emptyCondition, shouldNotify);
+ }
+ });
+ producerThreads.append(threadIdentifier);
+ }
+
+ for (ThreadIdentifier threadIdentifier : producerThreads)
+ waitForThreadCompletion(threadIdentifier);
+
+ {
+ std::lock_guard<Lock> locker(lock);
+ shouldContinue = false;
+ }
+ emptyCondition.notifyAll();
+
+ for (ThreadIdentifier threadIdentifier : consumerThreads)
+ waitForThreadCompletion(threadIdentifier);
+
+ EXPECT_EQ(numProducers * numMessagesPerProducer, received.size());
+ std::sort(received.begin(), received.end());
+ for (unsigned messageIndex = 0; messageIndex < numMessagesPerProducer; ++messageIndex) {
+ for (unsigned producerIndex = 0; producerIndex < numProducers; ++producerIndex)
+ EXPECT_EQ(messageIndex, received[messageIndex * numProducers + producerIndex]);
+ }
+}
+
+} // anonymous namespace
+
+TEST(WTF_Condition, OneProducerOneConsumerOneSlot)
+{
+ runTest(1, 1, 1, 100000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, OneProducerOneConsumerOneSlotTimeout)
+{
+ runTest(
+ 1, 1, 1, 100000, TacticallyNotifyAll,
+ std::chrono::microseconds(10000),
+ std::chrono::microseconds(1000000));
+}
+
+TEST(WTF_Condition, OneProducerOneConsumerHundredSlots)
+{
+ runTest(1, 1, 100, 1000000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, TenProducersOneConsumerOneSlot)
+{
+ runTest(10, 1, 1, 10000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, TenProducersOneConsumerHundredSlotsNotifyAll)
+{
+ runTest(10, 1, 100, 10000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, TenProducersOneConsumerHundredSlotsNotifyOne)
+{
+ runTest(10, 1, 100, 10000, AlwaysNotifyOne);
+}
+
+TEST(WTF_Condition, OneProducerTenConsumersOneSlot)
+{
+ runTest(1, 10, 1, 10000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, OneProducerTenConsumersHundredSlotsNotifyAll)
+{
+ runTest(1, 10, 100, 100000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, OneProducerTenConsumersHundredSlotsNotifyOne)
+{
+ runTest(1, 10, 100, 100000, AlwaysNotifyOne);
+}
+
+TEST(WTF_Condition, TenProducersTenConsumersOneSlot)
+{
+ runTest(10, 10, 1, 50000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, TenProducersTenConsumersHundredSlotsNotifyAll)
+{
+ runTest(10, 10, 100, 50000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, TenProducersTenConsumersHundredSlotsNotifyOne)
+{
+ runTest(10, 10, 100, 50000, AlwaysNotifyOne);
+}
+
+TEST(WTF_Condition, TimeoutTimesOut)
+{
+ Lock lock;
+ Condition condition;
+
+ lock.lock();
+ bool result = condition.waitFor(
+ lock, std::chrono::microseconds(10000), [] () -> bool { return false; });
+ lock.unlock();
+
+ EXPECT_FALSE(result);
+}
+
+} // namespace TestWebKitAPI
+
diff --git a/Tools/TestWebKitAPI/Tests/WTF/DateMath.cpp b/Tools/TestWebKitAPI/Tests/WTF/DateMath.cpp
new file mode 100644
index 000000000..463041d48
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/DateMath.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "Test.h"
+#include <wtf/DateMath.h>
+
+namespace TestWebKitAPI {
+
+// Note: The results of these function look weird if you do not understand the following mappings:
+// dayOfWeek: [0, 6] 0 being Monday, day: [1, 31], month: [0, 11], year: ex: 2011,
+// hours: [0, 23], minutes: [0, 59], seconds: [0, 59], utcOffset: [-720,720].
+
+TEST(WTF_DateMath, dateToDaysFrom1970)
+{
+ EXPECT_EQ(0.0, dateToDaysFrom1970(1970, 0, 1));
+ EXPECT_EQ(157.0, dateToDaysFrom1970(1970, 5, 7));
+ EXPECT_EQ(-145.0, dateToDaysFrom1970(1969, 7, 9));
+ EXPECT_EQ(16322, dateToDaysFrom1970(2014, 8, 9));
+}
+
+
+TEST(WTF_DateMath, isLeapYear)
+{
+ EXPECT_TRUE(isLeapYear(1804));
+ EXPECT_FALSE(isLeapYear(1900));
+ EXPECT_TRUE(isLeapYear(1968));
+ EXPECT_TRUE(isLeapYear(1976));
+ EXPECT_TRUE(isLeapYear(2000));
+ EXPECT_FALSE(isLeapYear(2010));
+ EXPECT_TRUE(isLeapYear(2012));
+ EXPECT_FALSE(isLeapYear(2100));
+}
+
+TEST(WTF_DateMath, msToYear)
+{
+ EXPECT_EQ(1962, msToYear(-220953600000));
+ EXPECT_EQ(1970, msToYear(0));
+ EXPECT_EQ(1970, msToYear(100));
+ EXPECT_EQ(1977, msToYear(220953600000));
+ EXPECT_EQ(2013, msToYear(1365318000000));
+}
+
+TEST(WTF_DateMath, msToDays)
+{
+ EXPECT_EQ(0, msToDays(0));
+ EXPECT_EQ(2557, msToDays(220953600000));
+ EXPECT_EQ(255, msToDays(22095360000));
+ EXPECT_EQ(25, msToDays(2209536000));
+ EXPECT_EQ(2, msToDays(220953600));
+ EXPECT_EQ(0, msToDays(22095360));
+ EXPECT_EQ(0, msToDays(2209536));
+}
+
+TEST(WTF_DateMath, msToMinutes)
+{
+ EXPECT_EQ(0, msToMinutes(0));
+ EXPECT_EQ(0, msToMinutes(220953600000));
+ EXPECT_EQ(36, msToMinutes(22095360000));
+ EXPECT_EQ(36, msToMinutes(22095360000));
+ EXPECT_EQ(45, msToMinutes(2209536000));
+ EXPECT_EQ(22, msToMinutes(220953600));
+ EXPECT_EQ(8, msToMinutes(22095360));
+ EXPECT_EQ(36, msToMinutes(2209536));
+}
+
+TEST(WTF_DateMath, msToHours)
+{
+ EXPECT_EQ(0, msToHours(0));
+ EXPECT_EQ(8, msToHours(220953600000));
+ EXPECT_EQ(17, msToHours(22095360000));
+ EXPECT_EQ(13, msToHours(2209536000));
+ EXPECT_EQ(13, msToHours(220953600));
+ EXPECT_EQ(6, msToHours(22095360));
+ EXPECT_EQ(0, msToHours(2209536));
+}
+
+TEST(WTF_DateMath, dayInYear)
+{
+ EXPECT_EQ(59, dayInYear(2015, 2, 1));
+ EXPECT_EQ(60, dayInYear(2012, 2, 1));
+ EXPECT_EQ(0, dayInYear(2015, 0, 1));
+ EXPECT_EQ(31, dayInYear(2015, 1, 1));
+}
+
+TEST(WTF_DateMath, monthFromDayInYear)
+{
+ EXPECT_EQ(2, monthFromDayInYear(59, false));
+ EXPECT_EQ(1, monthFromDayInYear(59, true));
+ EXPECT_EQ(2, monthFromDayInYear(60, true));
+ EXPECT_EQ(0, monthFromDayInYear(0, false));
+ EXPECT_EQ(0, monthFromDayInYear(0, true));
+ EXPECT_EQ(1, monthFromDayInYear(31, true));
+ EXPECT_EQ(1, monthFromDayInYear(31, false));
+}
+
+TEST(WTF_DateMath, dayInMonthFromDayInYear)
+{
+ EXPECT_EQ(1, dayInMonthFromDayInYear(0, false));
+ EXPECT_EQ(1, dayInMonthFromDayInYear(0, true));
+ EXPECT_EQ(1, dayInMonthFromDayInYear(59, false));
+ EXPECT_EQ(29, dayInMonthFromDayInYear(59, true));
+ EXPECT_EQ(1, dayInMonthFromDayInYear(60, true));
+ EXPECT_EQ(1, dayInMonthFromDayInYear(0, false));
+ EXPECT_EQ(1, dayInMonthFromDayInYear(0, true));
+ EXPECT_EQ(31, dayInMonthFromDayInYear(30, true));
+ EXPECT_EQ(31, dayInMonthFromDayInYear(30, false));
+ EXPECT_EQ(31, dayInMonthFromDayInYear(365, true));
+ EXPECT_EQ(32, dayInMonthFromDayInYear(365, false));
+ EXPECT_EQ(32, dayInMonthFromDayInYear(366, true));
+}
+
+TEST(WTF_DateMath, calculateLocalTimeOffset)
+{
+ // DST Start: April 30, 1967 (02:00 am)
+ LocalTimeOffset dstStart1967 = calculateLocalTimeOffset(-84301200000, WTF::LocalTime);
+ EXPECT_TRUE(dstStart1967.isDST);
+ EXPECT_EQ(-25200000, dstStart1967.offset);
+
+ // November 1, 1967 (02:00 am)
+ LocalTimeOffset dstAlmostEnd1967 = calculateLocalTimeOffset(-68317200000, WTF::LocalTime);
+ EXPECT_TRUE(dstAlmostEnd1967.isDST);
+ EXPECT_EQ(-25200000, dstAlmostEnd1967.offset);
+
+ // DST End: November 11, 1967 (02:00 am)
+ LocalTimeOffset dstEnd1967 = calculateLocalTimeOffset(-67536000000, WTF::LocalTime);
+ EXPECT_FALSE(dstEnd1967.isDST);
+ EXPECT_EQ(-25200000, dstStart1967.offset);
+
+ // DST Start: April 3, 1988 (02:00 am)
+ LocalTimeOffset dstStart1988 = calculateLocalTimeOffset(576054000000, WTF::LocalTime);
+ EXPECT_TRUE(dstStart1988.isDST);
+ EXPECT_EQ(-25200000, dstStart1988.offset);
+
+ // DST End: November 4, 2012 (02:00 am)
+ LocalTimeOffset dstEnd2012 = calculateLocalTimeOffset(1352012400000, WTF::LocalTime);
+ EXPECT_FALSE(dstEnd2012.isDST);
+ EXPECT_EQ(-28800000, dstEnd2012.offset);
+
+ // DST Begin: March 8, 2015
+ LocalTimeOffset dstBegin2015 = calculateLocalTimeOffset(1425801600000, WTF::LocalTime);
+ EXPECT_TRUE(dstBegin2015.isDST);
+ EXPECT_EQ(-25200000, dstBegin2015.offset);
+
+ LocalTimeOffset dstBegin2015UTC = calculateLocalTimeOffset(1425801600000, WTF::UTCTime);
+ EXPECT_FALSE(dstBegin2015UTC.isDST);
+ EXPECT_EQ(-28800000, dstBegin2015UTC.offset);
+
+ // DST End: November 1, 2015
+ LocalTimeOffset dstEnd2015 = calculateLocalTimeOffset(1446361200000, WTF::LocalTime);
+ EXPECT_FALSE(dstEnd2015.isDST);
+ EXPECT_EQ(-28800000, dstEnd2015.offset);
+
+ // DST Begin: March 13, 2016
+ LocalTimeOffset dstBegin2016 = calculateLocalTimeOffset(1458111600000, WTF::LocalTime);
+ EXPECT_TRUE(dstBegin2016.isDST);
+ EXPECT_EQ(-25200000, dstBegin2016.offset);
+
+ // DST End: November 6, 2016
+ LocalTimeOffset dstEnd2016 = calculateLocalTimeOffset(1478415600000, WTF::LocalTime);
+ EXPECT_FALSE(dstEnd2016.isDST);
+ EXPECT_EQ(-28800000, dstEnd2016.offset);
+
+ // DST Begin: March 12, 2017
+ LocalTimeOffset dstBegin2017 = calculateLocalTimeOffset(1489305600000, WTF::LocalTime);
+ EXPECT_TRUE(dstBegin2017.isDST);
+ EXPECT_EQ(-25200000, dstBegin2017.offset);
+
+ // DST End: November 5, 2017
+ LocalTimeOffset dstEnd2017 = calculateLocalTimeOffset(1509865200000, WTF::LocalTime);
+ EXPECT_FALSE(dstEnd2017.isDST);
+ EXPECT_EQ(-28800000, dstEnd2017.offset);
+
+ // DST Begin: March 11, 2018
+ LocalTimeOffset dstBegin2018 = calculateLocalTimeOffset(1520755200000, WTF::LocalTime);
+ EXPECT_TRUE(dstBegin2018.isDST);
+ EXPECT_EQ(-25200000, dstBegin2018.offset);
+
+ // DST End: November 4, 2018
+ LocalTimeOffset dstEnd2018 = calculateLocalTimeOffset(1541314800000, WTF::LocalTime);
+ EXPECT_FALSE(dstEnd2018.isDST);
+ EXPECT_EQ(-28800000, dstEnd2018.offset);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/Deque.cpp b/Tools/TestWebKitAPI/Tests/WTF/Deque.cpp
new file mode 100644
index 000000000..db9b0ac13
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/Deque.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "MoveOnly.h"
+#include <wtf/Deque.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF_Deque, Iterator)
+{
+ Deque<int> deque;
+ deque.append(11);
+ deque.prepend(10);
+ deque.append(12);
+ deque.append(13);
+
+ Deque<int>::iterator it = deque.begin();
+ Deque<int>::iterator end = deque.end();
+ EXPECT_TRUE(end != it);
+
+ EXPECT_EQ(10, *it);
+ ++it;
+ EXPECT_EQ(11, *it);
+ ++it;
+ EXPECT_EQ(12, *it);
+ ++it;
+ EXPECT_EQ(13, *it);
+ ++it;
+
+ EXPECT_TRUE(end == it);
+}
+
+TEST(WTF_Deque, InitializerList)
+{
+ Deque<int> deque = { 1, 2, 3, 4 };
+
+ EXPECT_EQ(4u, deque.size());
+
+ auto it = deque.begin();
+ auto end = deque.end();
+ EXPECT_TRUE(end != it);
+
+ EXPECT_EQ(1, *it);
+ ++it;
+ EXPECT_EQ(2, *it);
+ ++it;
+ EXPECT_EQ(3, *it);
+ ++it;
+ EXPECT_EQ(4, *it);
+ ++it;
+
+ EXPECT_TRUE(end == it);
+}
+
+TEST(WTF, DequeReverseIterator)
+{
+ Deque<int> deque;
+ deque.append(11);
+ deque.prepend(10);
+ deque.append(12);
+ deque.append(13);
+
+ Deque<int>::reverse_iterator it = deque.rbegin();
+ Deque<int>::reverse_iterator end = deque.rend();
+ EXPECT_TRUE(end != it);
+
+ EXPECT_EQ(13, *it);
+ ++it;
+ EXPECT_EQ(12, *it);
+ ++it;
+ EXPECT_EQ(11, *it);
+ ++it;
+ EXPECT_EQ(10, *it);
+ ++it;
+
+ EXPECT_TRUE(end == it);
+}
+
+TEST(WTF_Deque, Remove)
+{
+ Deque<int> deque;
+ deque.append(11);
+ deque.prepend(10);
+ deque.append(12);
+ deque.append(13);
+
+ EXPECT_EQ(10, deque.first());
+ EXPECT_EQ(13, deque.last());
+
+ deque.removeLast();
+ EXPECT_EQ(10, deque.first());
+ EXPECT_EQ(12, deque.last());
+
+ deque.removeFirst();
+ EXPECT_EQ(11, deque.first());
+ EXPECT_EQ(12, deque.last());
+
+ deque.removeFirst();
+ EXPECT_EQ(12, deque.first());
+ EXPECT_EQ(12, deque.last());
+
+ deque.removeLast();
+ EXPECT_TRUE(deque.isEmpty());
+}
+
+TEST(WTF_Deque, MoveOnly)
+{
+ Deque<MoveOnly> deque;
+
+ deque.append(MoveOnly(1));
+ deque.prepend(MoveOnly(0));
+
+ EXPECT_EQ(0U, deque.first().value());
+ EXPECT_EQ(1U, deque.last().value());
+
+ auto first = deque.takeFirst();
+ EXPECT_EQ(0U, first.value());
+
+ auto last = deque.takeLast();
+ EXPECT_EQ(1U, last.value());
+}
+
+TEST(WTF_Deque, MoveConstructor)
+{
+ Deque<MoveOnly, 4> deque;
+
+ for (unsigned i = 0; i < 10; ++i)
+ deque.append(MoveOnly(i));
+
+ EXPECT_EQ(10u, deque.size());
+
+ Deque<MoveOnly, 4> deque2 = WTF::move(deque);
+
+ EXPECT_EQ(10u, deque2.size());
+
+ unsigned i = 0;
+ for (auto& element : deque2) {
+ EXPECT_EQ(i, element.value());
+ ++i;
+ }
+}
+
+TEST(WTF_Deque, MoveAssignmentOperator)
+{
+ Deque<MoveOnly, 4> deque1;
+
+ for (unsigned i = 0; i < 10; ++i)
+ deque1.append(MoveOnly(i));
+
+ EXPECT_EQ(10u, deque1.size());
+
+ Deque<MoveOnly, 4> deque2;
+ for (unsigned i = 0; i < 10; ++i)
+ deque2.append(MoveOnly(i * 2));
+
+ deque1 = WTF::move(deque2);
+
+ EXPECT_EQ(10u, deque2.size());
+
+ unsigned i = 0;
+ for (auto& element : deque1) {
+ EXPECT_EQ(i * 2, element.value());
+ ++i;
+ }
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/Functional.cpp b/Tools/TestWebKitAPI/Tests/WTF/Functional.cpp
new file mode 100644
index 000000000..cf8c3c39c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/Functional.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <wtf/RefCounted.h>
+#include <wtf/Functional.h>
+
+namespace TestWebKitAPI {
+
+static int returnFortyTwo()
+{
+ return 42;
+}
+
+TEST(FunctionalTest, Basic)
+{
+ Function<int ()> emptyFunction;
+ ASSERT_TRUE(emptyFunction.isNull());
+
+ Function<int ()> returnFortyTwoFunction = bind(returnFortyTwo);
+ ASSERT_FALSE(returnFortyTwoFunction.isNull());
+ ASSERT_EQ(42, returnFortyTwoFunction());
+}
+
+static int multiplyByTwo(int n)
+{
+ return n * 2;
+}
+
+static double multiplyByOneAndAHalf(double d)
+{
+ return d * 1.5;
+}
+
+TEST(FunctionalTest, UnaryBind)
+{
+ Function<int ()> multiplyFourByTwoFunction = bind(multiplyByTwo, 4);
+ ASSERT_EQ(8, multiplyFourByTwoFunction());
+
+ Function<double ()> multiplyByOneAndAHalfFunction = bind(multiplyByOneAndAHalf, 3);
+ ASSERT_EQ(4.5, multiplyByOneAndAHalfFunction());
+}
+
+static int multiply(int x, int y)
+{
+ return x * y;
+}
+
+static int subtract(int x, int y)
+{
+ return x - y;
+}
+
+TEST(FunctionalTest, BinaryBind)
+{
+ Function<int ()> multiplyFourByTwoFunction = bind(multiply, 4, 2);
+ ASSERT_EQ(8, multiplyFourByTwoFunction());
+
+ Function<int ()> subtractTwoFromFourFunction = bind(subtract, 4, 2);
+ ASSERT_EQ(2, subtractTwoFromFourFunction());
+}
+
+class A {
+public:
+ explicit A(int i)
+ : m_i(i)
+ {
+ }
+
+ int f() { return m_i; }
+ int addF(int j) { return m_i + j; }
+
+private:
+ int m_i;
+};
+
+TEST(FunctionalTest, MemberFunctionBind)
+{
+ A a(10);
+ Function<int ()> function1 = bind(&A::f, &a);
+ ASSERT_EQ(10, function1());
+
+ Function<int ()> function2 = bind(&A::addF, &a, 15);
+ ASSERT_EQ(25, function2());
+}
+
+class B {
+public:
+ B()
+ : m_numRefCalls(0)
+ , m_numDerefCalls(0)
+ {
+ }
+
+ ~B()
+ {
+ }
+
+ void ref()
+ {
+ m_numRefCalls++;
+ }
+
+ void deref()
+ {
+ m_numDerefCalls++;
+ }
+
+ void f() { ASSERT_GT(m_numRefCalls, 0); }
+ void g(int) { ASSERT_GT(m_numRefCalls, 0); }
+
+ int m_numRefCalls;
+ int m_numDerefCalls;
+};
+
+TEST(FunctionalTest, MemberFunctionBindRefDeref)
+{
+ B b;
+
+ {
+ Function<void ()> function1 = bind(&B::f, &b);
+ function1();
+
+ Function<void ()> function2 = bind(&B::g, &b, 10);
+ function2();
+ }
+
+ ASSERT_TRUE(b.m_numRefCalls == b.m_numDerefCalls);
+ ASSERT_GT(b.m_numRefCalls, 0);
+
+}
+
+class Number : public RefCounted<Number> {
+public:
+ static PassRefPtr<Number> create(int value)
+ {
+ return adoptRef(new Number(value));
+ }
+
+ ~Number()
+ {
+ m_value = 0;
+ }
+
+ int value() const { return m_value; }
+
+private:
+ explicit Number(int value)
+ : m_value(value)
+ {
+ }
+
+ int m_value;
+};
+
+static int multiplyNumberByTwo(Number* number)
+{
+ return number->value() * 2;
+}
+
+TEST(FunctionalTest, RefCountedStorage)
+{
+ RefPtr<Number> five = Number::create(5);
+ Function<int ()> multiplyFiveByTwoFunction = bind(multiplyNumberByTwo, five);
+ ASSERT_EQ(10, multiplyFiveByTwoFunction());
+
+ Function<int ()> multiplyFourByTwoFunction = bind(multiplyNumberByTwo, Number::create(4));
+ ASSERT_EQ(8, multiplyFourByTwoFunction());
+
+ RefPtr<Number> six = Number::create(6);
+ Function<int ()> multiplySixByTwoFunction = bind(multiplyNumberByTwo, six.release());
+ ASSERT_FALSE(six);
+ ASSERT_EQ(12, multiplySixByTwoFunction());
+}
+
+namespace RefAndDerefTests {
+
+ template<typename T> struct RefCounted {
+ void ref();
+ void deref();
+ };
+ struct Connection : RefCounted<Connection> { };
+ COMPILE_ASSERT(WTF::HasRefAndDeref<Connection>::value, class_has_ref_and_deref);
+
+ struct NoRefOrDeref { };
+ COMPILE_ASSERT(!WTF::HasRefAndDeref<NoRefOrDeref>::value, class_has_no_ref_or_deref);
+
+ struct RefOnly { void ref(); };
+ COMPILE_ASSERT(!WTF::HasRefAndDeref<RefOnly>::value, class_has_ref_only);
+
+ struct DerefOnly { void deref(); };
+ COMPILE_ASSERT(!WTF::HasRefAndDeref<DerefOnly>::value, class_has_deref_only);
+
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp b/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp
new file mode 100644
index 000000000..a281f230a
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "Counters.h"
+#include "MoveOnly.h"
+#include "RefLogger.h"
+#include <string>
+#include <wtf/HashMap.h>
+#include <wtf/text/StringHash.h>
+
+namespace TestWebKitAPI {
+
+typedef WTF::HashMap<int, int> IntHashMap;
+
+TEST(WTF_HashMap, HashTableIteratorComparison)
+{
+ IntHashMap map;
+ map.add(1, 2);
+ ASSERT_TRUE(map.begin() != map.end());
+ ASSERT_FALSE(map.begin() == map.end());
+
+ IntHashMap::const_iterator begin = map.begin();
+ ASSERT_TRUE(begin == map.begin());
+ ASSERT_TRUE(map.begin() == begin);
+ ASSERT_TRUE(begin != map.end());
+ ASSERT_TRUE(map.end() != begin);
+ ASSERT_FALSE(begin != map.begin());
+ ASSERT_FALSE(map.begin() != begin);
+ ASSERT_FALSE(begin == map.end());
+ ASSERT_FALSE(map.end() == begin);
+}
+
+struct TestDoubleHashTraits : HashTraits<double> {
+ static const int minimumTableSize = 8;
+};
+
+typedef HashMap<double, int64_t, DefaultHash<double>::Hash, TestDoubleHashTraits> DoubleHashMap;
+
+static int bucketForKey(double key)
+{
+ return DefaultHash<double>::Hash::hash(key) & (TestDoubleHashTraits::minimumTableSize - 1);
+}
+
+TEST(WTF_HashMap, DoubleHashCollisions)
+{
+ // The "clobber" key here is one that ends up stealing the bucket that the -0 key
+ // originally wants to be in. This makes the 0 and -0 keys collide and the test then
+ // fails unless the FloatHash::equals() implementation can distinguish them.
+ const double clobberKey = 6;
+ const double zeroKey = 0;
+ const double negativeZeroKey = -zeroKey;
+
+ DoubleHashMap map;
+
+ map.add(clobberKey, 1);
+ map.add(zeroKey, 2);
+ map.add(negativeZeroKey, 3);
+
+ ASSERT_EQ(bucketForKey(clobberKey), bucketForKey(negativeZeroKey));
+ ASSERT_EQ(map.get(clobberKey), 1);
+ ASSERT_EQ(map.get(zeroKey), 2);
+ ASSERT_EQ(map.get(negativeZeroKey), 3);
+}
+
+TEST(WTF_HashMap, MoveOnlyValues)
+{
+ HashMap<unsigned, MoveOnly> moveOnlyValues;
+
+ for (size_t i = 0; i < 100; ++i) {
+ MoveOnly moveOnly(i + 1);
+ moveOnlyValues.set(i + 1, WTF::move(moveOnly));
+ }
+
+ for (size_t i = 0; i < 100; ++i) {
+ auto it = moveOnlyValues.find(i + 1);
+ ASSERT_FALSE(it == moveOnlyValues.end());
+ }
+
+ for (size_t i = 0; i < 50; ++i)
+ ASSERT_EQ(moveOnlyValues.take(i + 1).value(), i + 1);
+
+ for (size_t i = 50; i < 100; ++i)
+ ASSERT_TRUE(moveOnlyValues.remove(i + 1));
+
+ ASSERT_TRUE(moveOnlyValues.isEmpty());
+}
+
+TEST(WTF_HashMap, MoveOnlyKeys)
+{
+ HashMap<MoveOnly, unsigned> moveOnlyKeys;
+
+ for (size_t i = 0; i < 100; ++i) {
+ MoveOnly moveOnly(i + 1);
+ moveOnlyKeys.set(WTF::move(moveOnly), i + 1);
+ }
+
+ for (size_t i = 0; i < 100; ++i) {
+ auto it = moveOnlyKeys.find(MoveOnly(i + 1));
+ ASSERT_FALSE(it == moveOnlyKeys.end());
+ }
+
+ for (size_t i = 0; i < 100; ++i)
+ ASSERT_FALSE(moveOnlyKeys.add(MoveOnly(i + 1), i + 1).isNewEntry);
+
+ for (size_t i = 0; i < 100; ++i)
+ ASSERT_TRUE(moveOnlyKeys.remove(MoveOnly(i + 1)));
+
+ ASSERT_TRUE(moveOnlyKeys.isEmpty());
+}
+
+TEST(WTF_HashMap, InitializerList)
+{
+ HashMap<unsigned, std::string> map = {
+ { 1, "one" },
+ { 2, "two" },
+ { 3, "three" },
+ { 4, "four" },
+ };
+
+ EXPECT_EQ(4u, map.size());
+
+ EXPECT_EQ("one", map.get(1));
+ EXPECT_EQ("two", map.get(2));
+ EXPECT_EQ("three", map.get(3));
+ EXPECT_EQ("four", map.get(4));
+ EXPECT_EQ(std::string(), map.get(5));
+}
+
+TEST(WTF_HashMap, EfficientGetter)
+{
+ HashMap<unsigned, CopyMoveCounter> map;
+ map.set(1, CopyMoveCounter());
+
+ {
+ CopyMoveCounter::TestingScope scope;
+ map.get(1);
+ EXPECT_EQ(0U, CopyMoveCounter::constructionCount);
+ EXPECT_EQ(1U, CopyMoveCounter::copyCount);
+ EXPECT_EQ(0U, CopyMoveCounter::moveCount);
+ }
+
+ {
+ CopyMoveCounter::TestingScope scope;
+ map.get(2);
+ EXPECT_EQ(1U, CopyMoveCounter::constructionCount);
+ EXPECT_EQ(0U, CopyMoveCounter::copyCount);
+ EXPECT_EQ(1U, CopyMoveCounter::moveCount);
+ }
+}
+
+TEST(WTF_HashMap, UniquePtrKey)
+{
+ ConstructorDestructorCounter::TestingScope scope;
+
+ HashMap<std::unique_ptr<ConstructorDestructorCounter>, int> map;
+
+ auto uniquePtr = std::make_unique<ConstructorDestructorCounter>();
+ map.add(WTF::move(uniquePtr), 2);
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
+
+ map.clear();
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
+}
+
+TEST(WTF_HashMap, UniquePtrKey_CustomDeleter)
+{
+ ConstructorDestructorCounter::TestingScope constructorDestructorCounterScope;
+ DeleterCounter<ConstructorDestructorCounter>::TestingScope deleterCounterScope;
+
+ HashMap<std::unique_ptr<ConstructorDestructorCounter, DeleterCounter<ConstructorDestructorCounter>>, int> map;
+
+ std::unique_ptr<ConstructorDestructorCounter, DeleterCounter<ConstructorDestructorCounter>> uniquePtr(new ConstructorDestructorCounter(), DeleterCounter<ConstructorDestructorCounter>());
+ map.add(WTF::move(uniquePtr), 2);
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
+
+ EXPECT_EQ(0u, DeleterCounter<ConstructorDestructorCounter>::deleterCount);
+
+ map.clear();
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
+
+ EXPECT_EQ(1u, DeleterCounter<ConstructorDestructorCounter>::deleterCount);
+}
+
+TEST(WTF_HashMap, UniquePtrKey_FindUsingRawPointer)
+{
+ HashMap<std::unique_ptr<int>, int> map;
+
+ auto uniquePtr = std::make_unique<int>(5);
+ int* ptr = uniquePtr.get();
+ map.add(WTF::move(uniquePtr), 2);
+
+ auto it = map.find(ptr);
+ ASSERT_TRUE(it != map.end());
+ EXPECT_EQ(ptr, it->key.get());
+ EXPECT_EQ(2, it->value);
+}
+
+TEST(WTF_HashMap, UniquePtrKey_ContainsUsingRawPointer)
+{
+ HashMap<std::unique_ptr<int>, int> map;
+
+ auto uniquePtr = std::make_unique<int>(5);
+ int* ptr = uniquePtr.get();
+ map.add(WTF::move(uniquePtr), 2);
+
+ EXPECT_EQ(true, map.contains(ptr));
+}
+
+TEST(WTF_HashMap, UniquePtrKey_GetUsingRawPointer)
+{
+ HashMap<std::unique_ptr<int>, int> map;
+
+ auto uniquePtr = std::make_unique<int>(5);
+ int* ptr = uniquePtr.get();
+ map.add(WTF::move(uniquePtr), 2);
+
+ int value = map.get(ptr);
+ EXPECT_EQ(2, value);
+}
+
+TEST(WTF_HashMap, UniquePtrKey_RemoveUsingRawPointer)
+{
+ ConstructorDestructorCounter::TestingScope scope;
+
+ HashMap<std::unique_ptr<ConstructorDestructorCounter>, int> map;
+
+ auto uniquePtr = std::make_unique<ConstructorDestructorCounter>();
+ ConstructorDestructorCounter* ptr = uniquePtr.get();
+ map.add(WTF::move(uniquePtr), 2);
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
+
+ bool result = map.remove(ptr);
+ EXPECT_EQ(true, result);
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
+}
+
+TEST(WTF_HashMap, UniquePtrKey_TakeUsingRawPointer)
+{
+ ConstructorDestructorCounter::TestingScope scope;
+
+ HashMap<std::unique_ptr<ConstructorDestructorCounter>, int> map;
+
+ auto uniquePtr = std::make_unique<ConstructorDestructorCounter>();
+ ConstructorDestructorCounter* ptr = uniquePtr.get();
+ map.add(WTF::move(uniquePtr), 2);
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
+
+ int result = map.take(ptr);
+ EXPECT_EQ(2, result);
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
+}
+
+TEST(WTF_HashMap, RefPtrKey_Add)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+ RefPtr<RefLogger> ptr(&a);
+ map.add(ptr, 0);
+
+ ASSERT_STREQ("ref(a) ref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_AddUsingRelease)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+ RefPtr<RefLogger> ptr(&a);
+ map.add(ptr.release(), 0);
+
+ EXPECT_STREQ("ref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_AddUsingMove)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+ RefPtr<RefLogger> ptr(&a);
+ map.add(WTF::move(ptr), 0);
+
+ EXPECT_STREQ("ref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_AddUsingRaw)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+ RefPtr<RefLogger> ptr(&a);
+ map.add(ptr.get(), 0);
+
+ EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_AddKeyAlreadyPresent)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ map.add(ptr, 0);
+ }
+
+ EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr2(&a);
+ auto addResult = map.add(ptr2, 0);
+ EXPECT_FALSE(addResult.isNewEntry);
+ }
+
+ EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_AddUsingReleaseKeyAlreadyPresent)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ map.add(ptr, 0);
+ }
+
+ EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr2(&a);
+ auto addResult = map.add(ptr2.release(), 0);
+ EXPECT_FALSE(addResult.isNewEntry);
+ }
+
+ EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_AddUsingMoveKeyAlreadyPresent)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ map.add(ptr, 0);
+ }
+
+ EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr2(&a);
+ auto addResult = map.add(WTF::move(ptr2), 0);
+ EXPECT_FALSE(addResult.isNewEntry);
+ }
+
+ EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_Set)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+ RefPtr<RefLogger> ptr(&a);
+ map.set(ptr, 0);
+
+ ASSERT_STREQ("ref(a) ref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_SetUsingRelease)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+ RefPtr<RefLogger> ptr(&a);
+ map.set(ptr.release(), 0);
+
+ EXPECT_STREQ("ref(a) ", takeLogStr().c_str());
+}
+
+
+TEST(WTF_HashMap, RefPtrKey_SetUsingMove)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+ RefPtr<RefLogger> ptr(&a);
+ map.set(WTF::move(ptr), 0);
+
+ EXPECT_STREQ("ref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_SetUsingRaw)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+ RefPtr<RefLogger> ptr(&a);
+ map.set(ptr.get(), 0);
+
+ EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_SetKeyAlreadyPresent)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+
+ RefPtr<RefLogger> ptr(&a);
+ map.set(ptr, 0);
+
+ EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr2(&a);
+ auto addResult = map.set(ptr2, 1);
+ EXPECT_FALSE(addResult.isNewEntry);
+ EXPECT_EQ(1, map.get(ptr.get()));
+ }
+
+ EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_SetUsingReleaseKeyAlreadyPresent)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+
+ RefPtr<RefLogger> ptr(&a);
+ map.set(ptr, 0);
+
+ EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr2(&a);
+ auto addResult = map.set(ptr2.release(), 1);
+ EXPECT_FALSE(addResult.isNewEntry);
+ EXPECT_EQ(1, map.get(ptr.get()));
+ }
+
+ EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, RefPtrKey_SetUsingMoveKeyAlreadyPresent)
+{
+ HashMap<RefPtr<RefLogger>, int> map;
+
+ DerivedRefLogger a("a");
+
+ RefPtr<RefLogger> ptr(&a);
+ map.set(ptr, 0);
+
+ EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr2(&a);
+ auto addResult = map.set(WTF::move(ptr2), 1);
+ EXPECT_FALSE(addResult.isNewEntry);
+ EXPECT_EQ(1, map.get(ptr.get()));
+ }
+
+ EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp b/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp
new file mode 100644
index 000000000..79cb5e727
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "Counters.h"
+#include "MoveOnly.h"
+#include <wtf/HashSet.h>
+
+namespace TestWebKitAPI {
+
+template<int initialCapacity>
+ struct InitialCapacityTestHashTraits : public WTF::UnsignedWithZeroKeyHashTraits<int> {
+ static const int minimumTableSize = initialCapacity;
+};
+
+template<unsigned size>
+void testInitialCapacity()
+{
+ const unsigned initialCapacity = WTF::HashTableCapacityForSize<size>::value;
+ HashSet<int, DefaultHash<int>::Hash, InitialCapacityTestHashTraits<initialCapacity> > testSet;
+
+ // Initial capacity is null.
+ ASSERT_EQ(0u, testSet.capacity());
+
+ // Adding items up to size should never change the capacity.
+ for (size_t i = 0; i < size; ++i) {
+ testSet.add(i);
+ ASSERT_EQ(initialCapacity, static_cast<unsigned>(testSet.capacity()));
+ }
+
+ // Adding items up to less than half the capacity should not change the capacity.
+ unsigned capacityLimit = initialCapacity / 2 - 1;
+ for (size_t i = size; i < capacityLimit; ++i) {
+ testSet.add(i);
+ ASSERT_EQ(initialCapacity, static_cast<unsigned>(testSet.capacity()));
+ }
+
+ // Adding one more item increase the capacity.
+ testSet.add(initialCapacity);
+ EXPECT_GT(static_cast<unsigned>(testSet.capacity()), initialCapacity);
+}
+
+template<unsigned size> void generateTestCapacityUpToSize();
+template<> void generateTestCapacityUpToSize<0>()
+{
+}
+template<unsigned size> void generateTestCapacityUpToSize()
+{
+ generateTestCapacityUpToSize<size - 1>();
+ testInitialCapacity<size>();
+}
+
+TEST(WTF_HashSet, InitialCapacity)
+{
+ generateTestCapacityUpToSize<128>();
+}
+
+TEST(WTF_HashSet, MoveOnly)
+{
+ HashSet<MoveOnly> hashSet;
+
+ for (size_t i = 0; i < 100; ++i) {
+ MoveOnly moveOnly(i + 1);
+ hashSet.add(WTF::move(moveOnly));
+ }
+
+ for (size_t i = 0; i < 100; ++i)
+ EXPECT_TRUE(hashSet.contains(MoveOnly(i + 1)));
+
+ for (size_t i = 0; i < 100; ++i)
+ EXPECT_TRUE(hashSet.remove(MoveOnly(i + 1)));
+
+ EXPECT_TRUE(hashSet.isEmpty());
+
+ for (size_t i = 0; i < 100; ++i)
+ hashSet.add(MoveOnly(i + 1));
+
+ for (size_t i = 0; i < 100; ++i)
+ EXPECT_TRUE(hashSet.take(MoveOnly(i + 1)) == MoveOnly(i + 1));
+
+ EXPECT_TRUE(hashSet.isEmpty());
+
+ for (size_t i = 0; i < 100; ++i)
+ hashSet.add(MoveOnly(i + 1));
+
+ HashSet<MoveOnly> secondSet;
+
+ for (size_t i = 0; i < 100; ++i)
+ secondSet.add(hashSet.takeAny());
+
+ EXPECT_TRUE(hashSet.isEmpty());
+
+ for (size_t i = 0; i < 100; ++i)
+ EXPECT_TRUE(secondSet.contains(MoveOnly(i + 1)));
+}
+
+
+TEST(WTF_HashSet, UniquePtrKey)
+{
+ ConstructorDestructorCounter::TestingScope scope;
+
+ HashSet<std::unique_ptr<ConstructorDestructorCounter>> set;
+
+ auto uniquePtr = std::make_unique<ConstructorDestructorCounter>();
+ set.add(WTF::move(uniquePtr));
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
+
+ set.clear();
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
+}
+
+TEST(WTF_HashSet, UniquePtrKey_FindUsingRawPointer)
+{
+ HashSet<std::unique_ptr<int>> set;
+
+ auto uniquePtr = std::make_unique<int>(5);
+ int* ptr = uniquePtr.get();
+ set.add(WTF::move(uniquePtr));
+
+ auto it = set.find(ptr);
+ ASSERT_TRUE(it != set.end());
+ EXPECT_EQ(ptr, it->get());
+ EXPECT_EQ(5, *it->get());
+}
+
+TEST(WTF_HashSet, UniquePtrKey_ContainsUsingRawPointer)
+{
+ HashSet<std::unique_ptr<int>> set;
+
+ auto uniquePtr = std::make_unique<int>(5);
+ int* ptr = uniquePtr.get();
+ set.add(WTF::move(uniquePtr));
+
+ EXPECT_EQ(true, set.contains(ptr));
+}
+
+TEST(WTF_HashSet, UniquePtrKey_RemoveUsingRawPointer)
+{
+ ConstructorDestructorCounter::TestingScope scope;
+
+ HashSet<std::unique_ptr<ConstructorDestructorCounter>> set;
+
+ auto uniquePtr = std::make_unique<ConstructorDestructorCounter>();
+ ConstructorDestructorCounter* ptr = uniquePtr.get();
+ set.add(WTF::move(uniquePtr));
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
+
+ bool result = set.remove(ptr);
+ EXPECT_EQ(true, result);
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
+}
+
+TEST(WTF_HashSet, UniquePtrKey_TakeUsingRawPointer)
+{
+ ConstructorDestructorCounter::TestingScope scope;
+
+ HashSet<std::unique_ptr<ConstructorDestructorCounter>> set;
+
+ auto uniquePtr = std::make_unique<ConstructorDestructorCounter>();
+ ConstructorDestructorCounter* ptr = uniquePtr.get();
+ set.add(WTF::move(uniquePtr));
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
+
+ auto result = set.take(ptr);
+ EXPECT_EQ(ptr, result.get());
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
+
+ result = nullptr;
+
+ EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
+ EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
+}
+
+TEST(WTF_HashSet, CopyEmpty)
+{
+ {
+ HashSet<unsigned> foo;
+ HashSet<unsigned> bar(foo);
+
+ EXPECT_EQ(0u, bar.capacity());
+ EXPECT_EQ(0u, bar.size());
+ }
+ {
+ HashSet<unsigned> foo({ 1, 5, 64, 42 });
+ EXPECT_EQ(4u, foo.size());
+ foo.remove(1);
+ foo.remove(5);
+ foo.remove(42);
+ foo.remove(64);
+ HashSet<unsigned> bar(foo);
+
+ EXPECT_EQ(0u, bar.capacity());
+ EXPECT_EQ(0u, bar.size());
+ }
+}
+
+TEST(WTF_HashSet, CopyAllocateAtLeastMinimumCapacity)
+{
+ HashSet<unsigned> foo({ 42 });
+ EXPECT_EQ(1u, foo.size());
+ HashSet<unsigned> bar(foo);
+
+ EXPECT_EQ(8u, bar.capacity());
+ EXPECT_EQ(1u, bar.size());
+}
+
+TEST(WTF_HashSet, CopyCapacityIsNotOnBoundary)
+{
+ // Starting at 4 because the minimum size is 8.
+ // With a size of 8, a medium load can be up to 3.3333->3.
+ // Adding 1 to 3 would reach max load.
+ // While correct, that's not really what we care about here.
+ for (unsigned size = 4; size < 100; ++size) {
+ HashSet<unsigned> source;
+ for (unsigned i = 1; i < size + 1; ++i)
+ source.add(i);
+
+ HashSet<unsigned> copy1(source);
+ HashSet<unsigned> copy2(source);
+ HashSet<unsigned> copy3(source);
+
+ EXPECT_EQ(size, copy1.size());
+ EXPECT_EQ(size, copy2.size());
+ EXPECT_EQ(size, copy3.size());
+ for (unsigned i = 1; i < size + 1; ++i) {
+ EXPECT_TRUE(copy1.contains(i));
+ EXPECT_TRUE(copy2.contains(i));
+ EXPECT_TRUE(copy3.contains(i));
+ }
+ EXPECT_FALSE(copy1.contains(size + 2));
+ EXPECT_FALSE(copy2.contains(size + 2));
+ EXPECT_FALSE(copy3.contains(size + 2));
+
+ EXPECT_TRUE(copy2.remove(1));
+ EXPECT_EQ(copy1.capacity(), copy2.capacity());
+ EXPECT_FALSE(copy2.contains(1));
+
+ EXPECT_TRUE(copy3.add(size + 2).isNewEntry);
+ EXPECT_EQ(copy1.capacity(), copy3.capacity());
+ EXPECT_TRUE(copy3.contains(size + 2));
+ }
+}
+
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/IntegerToStringConversion.cpp b/Tools/TestWebKitAPI/Tests/WTF/IntegerToStringConversion.cpp
new file mode 100644
index 000000000..49a2de326
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/IntegerToStringConversion.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <limits>
+#include <wtf/StringExtras.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+template<typename IntegerType> struct PrintfFormatTrait { static const char format[]; };
+
+template<> struct PrintfFormatTrait<short> { static const char format[]; };
+const char PrintfFormatTrait<short>::format[] = "%hd";
+
+template<> struct PrintfFormatTrait<int> { static const char format[]; };
+const char PrintfFormatTrait<int>::format[] = "%d";
+
+template<> struct PrintfFormatTrait<long> { static const char format[]; };
+const char PrintfFormatTrait<long>::format[] = "%ld";
+
+template<> struct PrintfFormatTrait<long long> { static const char format[]; };
+#if OS(WINDOWS)
+const char PrintfFormatTrait<long long>::format[] = "%I64i";
+#else
+const char PrintfFormatTrait<long long>::format[] = "%lli";
+#endif // OS(WINDOWS)
+
+template<> struct PrintfFormatTrait<unsigned short> { static const char format[]; };
+const char PrintfFormatTrait<unsigned short>::format[] = "%hu";
+
+template<> struct PrintfFormatTrait<unsigned> { static const char format[]; };
+const char PrintfFormatTrait<unsigned>::format[] = "%u";
+
+template<> struct PrintfFormatTrait<unsigned long> { static const char format[]; };
+const char PrintfFormatTrait<unsigned long>::format[] = "%lu";
+
+template<> struct PrintfFormatTrait<unsigned long long> { static const char format[]; };
+#if OS(WINDOWS)
+const char PrintfFormatTrait<unsigned long long>::format[] = "%I64u";
+#else
+const char PrintfFormatTrait<unsigned long long>::format[] = "%llu";
+#endif // OS(WINDOWS)
+
+
+// FIXME: use snprintf from StringExtras.h
+template<typename IntegerType>
+void testBoundaries()
+{
+ const unsigned bufferSize = 256;
+ Vector<char, bufferSize> buffer;
+ buffer.resize(bufferSize);
+
+ const IntegerType min = std::numeric_limits<IntegerType>::min();
+ CString minStringData = String::number(min).latin1();
+ snprintf(buffer.data(), bufferSize, PrintfFormatTrait<IntegerType>::format, min);
+ ASSERT_STREQ(buffer.data(), minStringData.data());
+
+ const IntegerType max = std::numeric_limits<IntegerType>::max();
+ CString maxStringData = String::number(max).latin1();
+ snprintf(buffer.data(), bufferSize, PrintfFormatTrait<IntegerType>::format, max);
+ ASSERT_STREQ(buffer.data(), maxStringData.data());
+}
+
+template<typename IntegerType>
+void testNumbers()
+{
+ const unsigned bufferSize = 256;
+ Vector<char, bufferSize> buffer;
+ buffer.resize(bufferSize);
+
+ for (int i = -100; i < 100; ++i) {
+ const IntegerType number = static_cast<IntegerType>(i);
+ CString numberStringData = String::number(number).latin1();
+ snprintf(buffer.data(), bufferSize, PrintfFormatTrait<IntegerType>::format, number);
+ ASSERT_STREQ(buffer.data(), numberStringData.data());
+ }
+}
+
+TEST(WTF, IntegerToStringConversionSignedIntegerBoundaries)
+{
+ testBoundaries<short>();
+ testBoundaries<int>();
+ testBoundaries<long>();
+ testBoundaries<long long>();
+}
+
+TEST(WTF, IntegerToStringConversionSignedIntegerRegularNumbers)
+{
+ testNumbers<short>();
+ testNumbers<int>();
+ testNumbers<long>();
+ testNumbers<long long>();
+}
+
+TEST(WTF, IntegerToStringConversionUnsignedIntegerBoundaries)
+{
+ testBoundaries<unsigned short>();
+ testBoundaries<unsigned int>();
+ testBoundaries<unsigned long>();
+ testBoundaries<unsigned long long>();
+}
+
+TEST(WTF, IntegerToStringConversionUnsignedIntegerRegularNumbers)
+{
+ testNumbers<unsigned short>();
+ testNumbers<unsigned int>();
+ testNumbers<unsigned long>();
+ testNumbers<unsigned long long>();
+}
diff --git a/Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp b/Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp
new file mode 100644
index 000000000..d81dcfcfe
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "MoveOnly.h"
+#include <wtf/ListHashSet.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF_ListHashSet, RemoveFirst)
+{
+ ListHashSet<int> list;
+ list.add(1);
+ list.add(2);
+ list.add(3);
+
+ ASSERT_EQ(1, list.first());
+
+ list.removeFirst();
+ ASSERT_EQ(2, list.first());
+
+ list.removeFirst();
+ ASSERT_EQ(3, list.first());
+
+ list.removeFirst();
+ ASSERT_TRUE(list.isEmpty());
+}
+
+TEST(WTF_ListHashSet, RemoveLast)
+{
+ ListHashSet<int> list;
+ list.add(1);
+ list.add(2);
+ list.add(3);
+
+ ASSERT_EQ(3, list.last());
+
+ list.removeLast();
+ ASSERT_EQ(2, list.last());
+
+ list.removeLast();
+ ASSERT_EQ(1, list.last());
+
+ list.removeLast();
+ ASSERT_TRUE(list.isEmpty());
+}
+
+TEST(WTF_ListHashSet, AppendOrMoveToLastNewItems)
+{
+ ListHashSet<int> list;
+ ListHashSet<int>::AddResult result = list.appendOrMoveToLast(1);
+ ASSERT_TRUE(result.isNewEntry);
+ result = list.add(2);
+ ASSERT_TRUE(result.isNewEntry);
+ result = list.appendOrMoveToLast(3);
+ ASSERT_TRUE(result.isNewEntry);
+
+ ASSERT_EQ(list.size(), 3u);
+
+ // The list should be in order 1, 2, 3.
+ ListHashSet<int>::iterator iterator = list.begin();
+ ASSERT_EQ(1, *iterator);
+ ++iterator;
+ ASSERT_EQ(2, *iterator);
+ ++iterator;
+ ASSERT_EQ(3, *iterator);
+ ++iterator;
+}
+
+TEST(WTF_ListHashSet, AppendOrMoveToLastWithDuplicates)
+{
+ ListHashSet<int> list;
+
+ // Add a single element twice.
+ ListHashSet<int>::AddResult result = list.add(1);
+ ASSERT_TRUE(result.isNewEntry);
+ result = list.appendOrMoveToLast(1);
+ ASSERT_FALSE(result.isNewEntry);
+ ASSERT_EQ(1u, list.size());
+
+ list.add(2);
+ list.add(3);
+ ASSERT_EQ(3u, list.size());
+
+ // Appending 2 move it to the end.
+ ASSERT_EQ(3, list.last());
+ result = list.appendOrMoveToLast(2);
+ ASSERT_FALSE(result.isNewEntry);
+ ASSERT_EQ(2, list.last());
+
+ // Inverse the list by moving each element to end end.
+ result = list.appendOrMoveToLast(3);
+ ASSERT_FALSE(result.isNewEntry);
+ result = list.appendOrMoveToLast(2);
+ ASSERT_FALSE(result.isNewEntry);
+ result = list.appendOrMoveToLast(1);
+ ASSERT_FALSE(result.isNewEntry);
+ ASSERT_EQ(3u, list.size());
+
+ ListHashSet<int>::iterator iterator = list.begin();
+ ASSERT_EQ(3, *iterator);
+ ++iterator;
+ ASSERT_EQ(2, *iterator);
+ ++iterator;
+ ASSERT_EQ(1, *iterator);
+ ++iterator;
+}
+
+TEST(WTF_ListHashSet, PrependOrMoveToLastNewItems)
+{
+ ListHashSet<int> list;
+ ListHashSet<int>::AddResult result = list.prependOrMoveToFirst(1);
+ ASSERT_TRUE(result.isNewEntry);
+ result = list.add(2);
+ ASSERT_TRUE(result.isNewEntry);
+ result = list.prependOrMoveToFirst(3);
+ ASSERT_TRUE(result.isNewEntry);
+
+ ASSERT_EQ(list.size(), 3u);
+
+ // The list should be in order 3, 1, 2.
+ ListHashSet<int>::iterator iterator = list.begin();
+ ASSERT_EQ(3, *iterator);
+ ++iterator;
+ ASSERT_EQ(1, *iterator);
+ ++iterator;
+ ASSERT_EQ(2, *iterator);
+ ++iterator;
+}
+
+TEST(WTF_ListHashSet, PrependOrMoveToLastWithDuplicates)
+{
+ ListHashSet<int> list;
+
+ // Add a single element twice.
+ ListHashSet<int>::AddResult result = list.add(1);
+ ASSERT_TRUE(result.isNewEntry);
+ result = list.prependOrMoveToFirst(1);
+ ASSERT_FALSE(result.isNewEntry);
+ ASSERT_EQ(1u, list.size());
+
+ list.add(2);
+ list.add(3);
+ ASSERT_EQ(3u, list.size());
+
+ // Prepending 2 move it to the beginning.
+ ASSERT_EQ(1, list.first());
+ result = list.prependOrMoveToFirst(2);
+ ASSERT_FALSE(result.isNewEntry);
+ ASSERT_EQ(2, list.first());
+
+ // Inverse the list by moving each element to the first position.
+ result = list.prependOrMoveToFirst(1);
+ ASSERT_FALSE(result.isNewEntry);
+ result = list.prependOrMoveToFirst(2);
+ ASSERT_FALSE(result.isNewEntry);
+ result = list.prependOrMoveToFirst(3);
+ ASSERT_FALSE(result.isNewEntry);
+ ASSERT_EQ(3u, list.size());
+
+ ListHashSet<int>::iterator iterator = list.begin();
+ ASSERT_EQ(3, *iterator);
+ ++iterator;
+ ASSERT_EQ(2, *iterator);
+ ++iterator;
+ ASSERT_EQ(1, *iterator);
+ ++iterator;
+}
+
+TEST(WTF_ListHashSet, ReverseIterator)
+{
+ ListHashSet<int> list;
+
+ list.add(1);
+ list.add(2);
+ list.add(3);
+
+ auto it = list.rbegin();
+ ASSERT_EQ(3, *it);
+ ++it;
+ ASSERT_EQ(2, *it);
+ ++it;
+ ASSERT_EQ(1, *it);
+ ++it;
+ ASSERT_TRUE(it == list.rend());
+
+ const auto& listHashSet = list;
+
+ auto constIt = listHashSet.rbegin();
+ ASSERT_EQ(3, *constIt);
+ ++constIt;
+ ASSERT_EQ(2, *constIt);
+ ++constIt;
+ ASSERT_EQ(1, *constIt);
+ ++constIt;
+ ASSERT_TRUE(constIt == listHashSet.rend());
+}
+
+TEST(WTF_ListHashSet, MoveOnly)
+{
+ ListHashSet<MoveOnly> list;
+ list.add(MoveOnly(2));
+ list.add(MoveOnly(4));
+
+ // { 2, 4 }
+ ASSERT_EQ(2U, list.first().value());
+ ASSERT_EQ(4U, list.last().value());
+
+ list.appendOrMoveToLast(MoveOnly(3));
+
+ // { 2, 4, 3 }
+ ASSERT_EQ(3U, list.last().value());
+
+ // { 4, 3, 2 }
+ list.appendOrMoveToLast(MoveOnly(2));
+ ASSERT_EQ(4U, list.first().value());
+ ASSERT_EQ(2U, list.last().value());
+
+ list.prependOrMoveToFirst(MoveOnly(5));
+
+ // { 5, 2, 4, 3 }
+ ASSERT_EQ(5U, list.first().value());
+
+ list.prependOrMoveToFirst(MoveOnly(3));
+
+ // { 3, 5, 4, 2 }
+ ASSERT_EQ(3U, list.first().value());
+ ASSERT_EQ(2U, list.last().value());
+
+ list.insertBefore(MoveOnly(4), MoveOnly(1));
+ list.insertBefore(list.end(), MoveOnly(6));
+
+ // { 3, 5, 1, 4, 2, 6 }
+ ASSERT_EQ(3U, list.takeFirst().value());
+ ASSERT_EQ(5U, list.takeFirst().value());
+ ASSERT_EQ(1U, list.takeFirst().value());
+
+ // { 4, 2, 6 }
+ ASSERT_EQ(6U, list.takeLast().value());
+ ASSERT_EQ(2U, list.takeLast().value());
+ ASSERT_EQ(4U, list.takeLast().value());
+
+ ASSERT_TRUE(list.isEmpty());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp b/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp
new file mode 100644
index 000000000..fcf8a4bea
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <wtf/Lock.h>
+#include <wtf/Threading.h>
+#include <wtf/ThreadingPrimitives.h>
+#include <wtf/WordLock.h>
+
+using namespace WTF;
+
+namespace TestWebKitAPI {
+
+struct LockInspector {
+ template<typename LockType>
+ static bool isFullyReset(LockType& lock)
+ {
+ return lock.isFullyReset();
+ }
+};
+
+template<typename LockType>
+void runLockTest(unsigned numThreadGroups, unsigned numThreadsPerGroup, unsigned workPerCriticalSection, unsigned numIterations)
+{
+ std::unique_ptr<LockType[]> locks = std::make_unique<LockType[]>(numThreadGroups);
+ std::unique_ptr<double[]> words = std::make_unique<double[]>(numThreadGroups);
+ std::unique_ptr<ThreadIdentifier[]> threads = std::make_unique<ThreadIdentifier[]>(numThreadGroups * numThreadsPerGroup);
+
+ for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) {
+ words[threadGroupIndex] = 0;
+
+ for (unsigned threadIndex = numThreadsPerGroup; threadIndex--;) {
+ threads[threadGroupIndex * numThreadsPerGroup + threadIndex] = createThread(
+ "Lock test thread",
+ [threadGroupIndex, &locks, &words, numIterations, workPerCriticalSection] () {
+ for (unsigned i = numIterations; i--;) {
+ locks[threadGroupIndex].lock();
+ for (unsigned j = workPerCriticalSection; j--;)
+ words[threadGroupIndex]++;
+ locks[threadGroupIndex].unlock();
+ }
+ });
+ }
+ }
+
+ for (unsigned threadIndex = numThreadGroups * numThreadsPerGroup; threadIndex--;)
+ waitForThreadCompletion(threads[threadIndex]);
+
+ double expected = 0;
+ for (uint64_t i = static_cast<uint64_t>(numIterations) * workPerCriticalSection * numThreadsPerGroup; i--;)
+ expected++;
+
+ for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;)
+ EXPECT_EQ(expected, words[threadGroupIndex]);
+
+ // Now test that the locks correctly reset themselves. We expect that if a single thread locks
+ // each of the locks twice in a row, then the lock should be in a pristine state.
+ for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) {
+ for (unsigned i = 2; i--;) {
+ locks[threadGroupIndex].lock();
+ locks[threadGroupIndex].unlock();
+ }
+
+ EXPECT_EQ(true, LockInspector::isFullyReset(locks[threadGroupIndex]));
+ }
+}
+
+TEST(WTF_WordLock, UncontendedShortSection)
+{
+ runLockTest<WordLock>(1, 1, 1, 10000000);
+}
+
+TEST(WTF_WordLock, UncontendedLongSection)
+{
+ runLockTest<WordLock>(1, 1, 10000, 1000);
+}
+
+TEST(WTF_WordLock, ContendedShortSection)
+{
+ runLockTest<WordLock>(1, 10, 1, 5000000);
+}
+
+TEST(WTF_WordLock, ContendedLongSection)
+{
+ runLockTest<WordLock>(1, 10, 10000, 10000);
+}
+
+TEST(WTF_WordLock, ManyContendedShortSections)
+{
+ runLockTest<WordLock>(10, 10, 1, 500000);
+}
+
+TEST(WTF_WordLock, ManyContendedLongSections)
+{
+ runLockTest<WordLock>(10, 10, 10000, 500);
+}
+
+TEST(WTF_Lock, UncontendedShortSection)
+{
+ runLockTest<Lock>(1, 1, 1, 10000000);
+}
+
+TEST(WTF_Lock, UncontendedLongSection)
+{
+ runLockTest<Lock>(1, 1, 10000, 1000);
+}
+
+TEST(WTF_Lock, ContendedShortSection)
+{
+ runLockTest<Lock>(1, 10, 1, 10000000);
+}
+
+TEST(WTF_Lock, ContendedLongSection)
+{
+ runLockTest<Lock>(1, 10, 10000, 10000);
+}
+
+TEST(WTF_Lock, ManyContendedShortSections)
+{
+ runLockTest<Lock>(10, 10, 1, 500000);
+}
+
+TEST(WTF_Lock, ManyContendedLongSections)
+{
+ runLockTest<Lock>(10, 10, 10000, 1000);
+}
+
+TEST(WTF_Lock, ManyContendedLongerSections)
+{
+ runLockTest<Lock>(10, 10, 100000, 1);
+}
+
+TEST(WTF_Lock, SectionAddressCollision)
+{
+ runLockTest<Lock>(4, 2, 10000, 2000);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/MD5.cpp b/Tools/TestWebKitAPI/Tests/WTF/MD5.cpp
new file mode 100644
index 000000000..2c862a9cf
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/MD5.cpp
@@ -0,0 +1,47 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ */
+
+#include "config.h"
+#include <wtf/MD5.h>
+#include <wtf/StringExtras.h>
+#include <wtf/text/CString.h>
+
+namespace TestWebKitAPI {
+
+static void expectMD5(CString input, CString expected)
+{
+ MD5 md5;
+ md5.addBytes(reinterpret_cast<const uint8_t*>(input.data()), input.length());
+ MD5::Digest digest;
+ md5.checksum(digest);
+ char* buf = 0;
+ CString actual = CString::newUninitialized(32, buf);
+ for (size_t i = 0; i < MD5::hashSize; i++, buf += 2)
+ snprintf(buf, 3, "%02x", digest[i]);
+
+ ASSERT_EQ(expected.length(), actual.length());
+ ASSERT_STREQ(expected.data(), actual.data());
+}
+
+TEST(WTF_MD5, Computation)
+{
+ // MD5 Test suite from http://www.ietf.org/rfc/rfc1321.txt.
+ expectMD5("", "d41d8cd98f00b204e9800998ecf8427e");
+ expectMD5("a", "0cc175b9c0f1b6a831c399e269772661");
+ expectMD5("abc", "900150983cd24fb0d6963f7d28e17f72");
+ expectMD5("message digest", "f96b697d7cb7938d525a2f31aaf161d0");
+ expectMD5("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b");
+ expectMD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "d174ab98d277d9f5a5611c2c9f419d9f");
+ expectMD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a");
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/MathExtras.cpp b/Tools/TestWebKitAPI/Tests/WTF/MathExtras.cpp
new file mode 100644
index 000000000..7cbb29766
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/MathExtras.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/MathExtras.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF, Lrint)
+{
+ EXPECT_EQ(lrint(-7.5), -8);
+ EXPECT_EQ(lrint(-8.5), -8);
+ EXPECT_EQ(lrint(-0.5), 0);
+ EXPECT_EQ(lrint(0.5), 0);
+ EXPECT_EQ(lrint(-0.5), 0);
+ EXPECT_EQ(lrint(1.3), 1);
+ EXPECT_EQ(lrint(1.7), 2);
+ EXPECT_EQ(lrint(0), 0);
+ EXPECT_EQ(lrint(-0), 0);
+ if (sizeof(long int) == 8) {
+ // Largest double number with 0.5 precision and one halfway rounding case below.
+ EXPECT_EQ(lrint(pow(2.0, 52) - 0.5), pow(2.0, 52));
+ EXPECT_EQ(lrint(pow(2.0, 52) - 1.5), pow(2.0, 52) - 2);
+ // Smallest double number with 0.5 precision and one halfway rounding case above.
+ EXPECT_EQ(lrint(-pow(2.0, 52) + 0.5), -pow(2.0, 52));
+ EXPECT_EQ(lrint(-pow(2.0, 52) + 1.5), -pow(2.0, 52) + 2);
+ }
+}
+
+TEST(WTF, clampToIntLong)
+{
+ if (sizeof(long) == sizeof(int))
+ return;
+
+ long maxInt = std::numeric_limits<int>::max();
+ long minInt = std::numeric_limits<int>::min();
+ long overflowInt = maxInt + 1;
+ long underflowInt = minInt - 1;
+
+ EXPECT_GT(overflowInt, maxInt);
+ EXPECT_LT(underflowInt, minInt);
+
+ EXPECT_EQ(clampTo<int>(maxInt), maxInt);
+ EXPECT_EQ(clampTo<int>(minInt), minInt);
+
+ EXPECT_EQ(clampTo<int>(overflowInt), maxInt);
+ EXPECT_EQ(clampTo<int>(underflowInt), minInt);
+}
+
+TEST(WTF, clampToIntLongLong)
+{
+ long long maxInt = std::numeric_limits<int>::max();
+ long long minInt = std::numeric_limits<int>::min();
+ long long overflowInt = maxInt + 1;
+ long long underflowInt = minInt - 1;
+
+ EXPECT_GT(overflowInt, maxInt);
+ EXPECT_LT(underflowInt, minInt);
+
+ EXPECT_EQ(clampTo<int>(maxInt), maxInt);
+ EXPECT_EQ(clampTo<int>(minInt), minInt);
+
+ EXPECT_EQ(clampTo<int>(overflowInt), maxInt);
+ EXPECT_EQ(clampTo<int>(underflowInt), minInt);
+}
+
+TEST(WTF, clampToIntegerFloat)
+{
+ // This test is inaccurate as floats will round the min / max integer
+ // due to the narrow mantissa. However it will properly checks within
+ // (close to the extreme) and outside the integer range.
+ float maxInt = std::numeric_limits<int>::max();
+ float minInt = std::numeric_limits<int>::min();
+ float overflowInt = maxInt * 1.1;
+ float underflowInt = minInt * 1.1;
+
+ EXPECT_GT(overflowInt, maxInt);
+ EXPECT_LT(underflowInt, minInt);
+
+ // If maxInt == 2^31 - 1 (ie on I32 architecture), the closest float used to represent it is 2^31.
+ EXPECT_NEAR(clampToInteger(maxInt), maxInt, 1);
+ EXPECT_EQ(clampToInteger(minInt), minInt);
+
+ EXPECT_NEAR(clampToInteger(overflowInt), maxInt, 1);
+ EXPECT_EQ(clampToInteger(underflowInt), minInt);
+}
+
+TEST(WTF, clampToIntegerDouble)
+{
+ double maxInt = std::numeric_limits<int>::max();
+ double minInt = std::numeric_limits<int>::min();
+ double overflowInt = maxInt + 1;
+ double underflowInt = minInt - 1;
+
+ EXPECT_GT(overflowInt, maxInt);
+ EXPECT_LT(underflowInt, minInt);
+
+ EXPECT_EQ(clampToInteger(maxInt), maxInt);
+ EXPECT_EQ(clampToInteger(minInt), minInt);
+
+ EXPECT_EQ(clampToInteger(overflowInt), maxInt);
+ EXPECT_EQ(clampToInteger(underflowInt), minInt);
+}
+
+TEST(WTF, clampToFloat)
+{
+ double maxFloat = std::numeric_limits<float>::max();
+ double minFloat = -maxFloat;
+ double overflowFloat = maxFloat * 1.1;
+ double underflowFloat = minFloat * 1.1;
+
+ EXPECT_GT(overflowFloat, maxFloat);
+ EXPECT_LT(underflowFloat, minFloat);
+
+ EXPECT_EQ(clampToFloat(maxFloat), maxFloat);
+ EXPECT_EQ(clampToFloat(minFloat), minFloat);
+
+ EXPECT_EQ(clampToFloat(overflowFloat), maxFloat);
+ EXPECT_EQ(clampToFloat(underflowFloat), minFloat);
+
+ EXPECT_EQ(clampToFloat(std::numeric_limits<float>::infinity()), maxFloat);
+ EXPECT_EQ(clampToFloat(-std::numeric_limits<float>::infinity()), minFloat);
+}
+
+TEST(WTF, clampToUnsignedLong)
+{
+ if (sizeof(unsigned long) == sizeof(unsigned))
+ return;
+
+ unsigned long maxUnsigned = std::numeric_limits<unsigned>::max();
+ unsigned long overflowUnsigned = maxUnsigned + 1;
+
+ EXPECT_GT(overflowUnsigned, maxUnsigned);
+
+ EXPECT_EQ(clampTo<unsigned>(maxUnsigned), maxUnsigned);
+
+ EXPECT_EQ(clampTo<unsigned>(overflowUnsigned), maxUnsigned);
+ EXPECT_EQ(clampTo<unsigned>(-1), 0u);
+}
+
+TEST(WTF, clampToUnsignedLongLong)
+{
+ unsigned long long maxUnsigned = std::numeric_limits<unsigned>::max();
+ unsigned long long overflowUnsigned = maxUnsigned + 1;
+
+ EXPECT_GT(overflowUnsigned, maxUnsigned);
+
+ EXPECT_EQ(clampTo<unsigned>(maxUnsigned), maxUnsigned);
+
+ EXPECT_EQ(clampTo<unsigned>(overflowUnsigned), maxUnsigned);
+ EXPECT_EQ(clampTo<unsigned>(-1), 0u);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp b/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp
new file mode 100644
index 000000000..248fa333b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _USE_MATH_DEFINES 1
+#include "config.h"
+
+#include <wtf/MediaTime.h>
+
+using namespace std;
+
+namespace WTF {
+
+std::ostream& operator<<(std::ostream& out, const MediaTime& val)
+{
+ out << "{ ";
+ if (val.isInvalid())
+ out << "invalid";
+ else if (val.isPositiveInfinite())
+ out << "+infinite";
+ else if (val.isNegativeInfinite())
+ out << "-infinite";
+ else if (val.hasDoubleValue())
+ out << "double: " << val.toDouble();
+ else
+ out << "value: " << val.timeValue() << ", scale: " << val.timeScale();
+ return out << " }";
+}
+
+}
+
+namespace TestWebKitAPI {
+
+TEST(WTF, MediaTime)
+{
+ // Comparison Operators
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() > MediaTime::negativeInfiniteTime(), true);
+ EXPECT_EQ(MediaTime::negativeInfiniteTime() < MediaTime::positiveInfiniteTime(), true);
+ EXPECT_EQ(MediaTime::negativeInfiniteTime() == MediaTime::negativeInfiniteTime(), true);
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() == MediaTime::positiveInfiniteTime(), true);
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() != MediaTime::negativeInfiniteTime(), true);
+ EXPECT_EQ(MediaTime::invalidTime() == MediaTime::invalidTime(), true);
+ EXPECT_EQ(MediaTime::invalidTime() != MediaTime::invalidTime(), false);
+ EXPECT_EQ(MediaTime::invalidTime() != MediaTime::zeroTime(), true);
+ EXPECT_EQ(MediaTime::invalidTime() > MediaTime::negativeInfiniteTime(), true);
+ EXPECT_EQ(MediaTime::invalidTime() > MediaTime::positiveInfiniteTime(), true);
+ EXPECT_EQ(MediaTime::negativeInfiniteTime() < MediaTime::invalidTime(), true);
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() < MediaTime::invalidTime(), true);
+ EXPECT_EQ(MediaTime::indefiniteTime() == MediaTime::indefiniteTime(), true);
+ EXPECT_EQ(MediaTime::indefiniteTime() != MediaTime::indefiniteTime(), false);
+ EXPECT_EQ(MediaTime::indefiniteTime() != MediaTime::zeroTime(), true);
+ EXPECT_EQ(MediaTime::indefiniteTime() > MediaTime::negativeInfiniteTime(), true);
+ EXPECT_EQ(MediaTime::indefiniteTime() < MediaTime::positiveInfiniteTime(), true);
+ EXPECT_EQ(MediaTime::negativeInfiniteTime() < MediaTime::indefiniteTime(), true);
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() > MediaTime::indefiniteTime(), true);
+ EXPECT_EQ(MediaTime(1, 1) < MediaTime::indefiniteTime(), true);
+ EXPECT_EQ(MediaTime::indefiniteTime() > MediaTime(1, 1), true);
+ EXPECT_EQ(MediaTime(1, 1) < MediaTime(2, 1), true);
+ EXPECT_EQ(MediaTime(2, 1) > MediaTime(1, 1), true);
+ EXPECT_EQ(MediaTime(1, 1) != MediaTime(2, 1), true);
+ EXPECT_EQ(MediaTime(2, 1) == MediaTime(2, 1), true);
+ EXPECT_EQ(MediaTime(2, 1) == MediaTime(4, 2), true);
+
+ // Addition Operators
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() + MediaTime::positiveInfiniteTime(), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime::negativeInfiniteTime() + MediaTime::negativeInfiniteTime(), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() + MediaTime::negativeInfiniteTime(), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::negativeInfiniteTime() + MediaTime::positiveInfiniteTime(), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() + MediaTime(1, 1), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime(1, 1) + MediaTime::positiveInfiniteTime(), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime::negativeInfiniteTime() + MediaTime(1, 1), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime(1, 1) + MediaTime::negativeInfiniteTime(), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime::invalidTime() + MediaTime::positiveInfiniteTime(), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::invalidTime() + MediaTime::negativeInfiniteTime(), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::invalidTime() + MediaTime::invalidTime(), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::invalidTime() + MediaTime(1, 1), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::indefiniteTime() + MediaTime::positiveInfiniteTime(), MediaTime::indefiniteTime());
+ EXPECT_EQ(MediaTime::indefiniteTime() + MediaTime::negativeInfiniteTime(), MediaTime::indefiniteTime());
+ EXPECT_EQ(MediaTime::indefiniteTime() + MediaTime::indefiniteTime(), MediaTime::indefiniteTime());
+ EXPECT_EQ(MediaTime::indefiniteTime() + MediaTime(1, 1), MediaTime::indefiniteTime());
+ EXPECT_EQ(MediaTime(1, 1) + MediaTime(1, 1), MediaTime(2, 1));
+ EXPECT_EQ(MediaTime(1, 2) + MediaTime(1, 3), MediaTime(5, 6));
+ EXPECT_EQ(MediaTime(1, numeric_limits<int32_t>::max()-1) + MediaTime(1, numeric_limits<int32_t>::max()-2), MediaTime(2, numeric_limits<int32_t>::max()));
+
+ // Subtraction Operators
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() - MediaTime::positiveInfiniteTime(), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::negativeInfiniteTime() - MediaTime::negativeInfiniteTime(), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() - MediaTime::negativeInfiniteTime(), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime::negativeInfiniteTime() - MediaTime::positiveInfiniteTime(), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime::positiveInfiniteTime() - MediaTime(1, 1), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime(1, 1) - MediaTime::positiveInfiniteTime(), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime::negativeInfiniteTime() - MediaTime(1, 1), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime(1, 1) - MediaTime::negativeInfiniteTime(), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime::invalidTime() - MediaTime::positiveInfiniteTime(), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::invalidTime() - MediaTime::negativeInfiniteTime(), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::invalidTime() - MediaTime::invalidTime(), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::invalidTime() - MediaTime(1, 1), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::indefiniteTime() - MediaTime::positiveInfiniteTime(), MediaTime::indefiniteTime());
+ EXPECT_EQ(MediaTime::indefiniteTime() - MediaTime::negativeInfiniteTime(), MediaTime::indefiniteTime());
+ EXPECT_EQ(MediaTime::indefiniteTime() - MediaTime::indefiniteTime(), MediaTime::indefiniteTime());
+ EXPECT_EQ(MediaTime::indefiniteTime() - MediaTime(1, 1), MediaTime::indefiniteTime());
+ EXPECT_EQ(MediaTime(3, 1) - MediaTime(2, 1), MediaTime(1, 1));
+ EXPECT_EQ(MediaTime(1, 2) - MediaTime(1, 3), MediaTime(1, 6));
+ EXPECT_EQ(MediaTime(2, numeric_limits<int32_t>::max()-1) - MediaTime(1, numeric_limits<int32_t>::max()-2), MediaTime(1, numeric_limits<int32_t>::max()));
+
+ // Multiplication Operators
+ EXPECT_EQ(MediaTime::positiveInfiniteTime(), MediaTime::positiveInfiniteTime() * 2);
+ EXPECT_EQ(MediaTime::negativeInfiniteTime(), MediaTime::negativeInfiniteTime() * 2);
+ EXPECT_EQ(MediaTime::negativeInfiniteTime(), MediaTime::positiveInfiniteTime() * -2);
+ EXPECT_EQ(MediaTime::positiveInfiniteTime(), MediaTime::negativeInfiniteTime() * -2);
+ EXPECT_EQ(MediaTime::invalidTime(), MediaTime::invalidTime() * 2);
+ EXPECT_EQ(MediaTime::invalidTime(), MediaTime::invalidTime() * -2);
+ EXPECT_EQ(MediaTime::indefiniteTime(), MediaTime::indefiniteTime() * 2);
+ EXPECT_EQ(MediaTime::indefiniteTime(), MediaTime::indefiniteTime() * -2);
+ EXPECT_EQ(MediaTime(6, 1), MediaTime(3, 1) * 2);
+ EXPECT_EQ(MediaTime(0, 1), MediaTime(0, 1) * 2);
+ EXPECT_EQ(MediaTime(int64_t(1) << 60, 1), MediaTime(int64_t(1) << 60, 2) * 2);
+ EXPECT_EQ(MediaTime::positiveInfiniteTime(), MediaTime(numeric_limits<int64_t>::max(), 1) * 2);
+ EXPECT_EQ(MediaTime::positiveInfiniteTime(), MediaTime(numeric_limits<int64_t>::min(), 1) * -2);
+ EXPECT_EQ(MediaTime::negativeInfiniteTime(), MediaTime(numeric_limits<int64_t>::max(), 1) * -2);
+ EXPECT_EQ(MediaTime::negativeInfiniteTime(), MediaTime(numeric_limits<int64_t>::min(), 1) * 2);
+
+ // Constants
+ EXPECT_EQ(MediaTime::zeroTime(), MediaTime(0, 1));
+ EXPECT_EQ(MediaTime::invalidTime(), MediaTime(-1, 1, 0));
+ EXPECT_EQ(MediaTime::positiveInfiniteTime(), MediaTime(0, 1, MediaTime::PositiveInfinite));
+ EXPECT_EQ(MediaTime::negativeInfiniteTime(), MediaTime(0, 1, MediaTime::NegativeInfinite));
+ EXPECT_EQ(MediaTime::indefiniteTime(), MediaTime(0, 1, MediaTime::Indefinite));
+
+ // Absolute Functions
+ EXPECT_EQ(abs(MediaTime::positiveInfiniteTime()), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(abs(MediaTime::negativeInfiniteTime()), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(abs(MediaTime::invalidTime()), MediaTime::invalidTime());
+ EXPECT_EQ(abs(MediaTime(1, 1)), MediaTime(1, 1));
+ EXPECT_EQ(abs(MediaTime(-1, 1)), MediaTime(1, 1));
+ EXPECT_EQ(abs(MediaTime(-1, -1)), MediaTime(-1, -1));
+ EXPECT_EQ(abs(MediaTime(1, -1)), MediaTime(-1, -1));
+
+ // Floating Point Functions
+ EXPECT_EQ(MediaTime::createWithFloat(1.0f), MediaTime(1, 1));
+ EXPECT_EQ(MediaTime::createWithFloat(1.5f), MediaTime(3, 2));
+ EXPECT_EQ(MediaTime::createWithDouble(1.0), MediaTime(1, 1));
+ EXPECT_EQ(MediaTime::createWithDouble(1.5), MediaTime(3, 2));
+ EXPECT_EQ(MediaTime(1, 1).toFloat(), 1.0f);
+ EXPECT_EQ(MediaTime(3, 2).toFloat(), 1.5f);
+ EXPECT_EQ(MediaTime(1, 1).toDouble(), 1.0);
+ EXPECT_EQ(MediaTime(3, 2).toDouble(), 1.5);
+ EXPECT_EQ(MediaTime(1, 1 << 16).toFloat(), 1 / pow(2.0f, 16.0f));
+ EXPECT_EQ(MediaTime(1, 1 << 30).toDouble(), 1 / pow(2.0, 30.0));
+ EXPECT_EQ(MediaTime::createWithDouble(M_PI, 1 << 30), MediaTime(3373259426U, 1 << 30));
+ EXPECT_EQ(MediaTime::createWithFloat(INFINITY), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime::createWithFloat(-INFINITY), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime::createWithFloat(NAN), MediaTime::invalidTime());
+ EXPECT_EQ(MediaTime::createWithDouble(INFINITY), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime::createWithDouble(-INFINITY), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime::createWithDouble(NAN), MediaTime::invalidTime());
+
+ // Floating Point Round Trip
+ EXPECT_EQ(10.0123456789f, MediaTime::createWithFloat(10.0123456789f).toFloat());
+ EXPECT_EQ(10.0123456789, MediaTime::createWithDouble(10.0123456789).toDouble());
+
+ // Floating Point Math
+ EXPECT_EQ(1.5 + 3.3, (MediaTime::createWithDouble(1.5) + MediaTime::createWithDouble(3.3)).toDouble());
+ EXPECT_EQ(1.5 - 3.3, (MediaTime::createWithDouble(1.5) - MediaTime::createWithDouble(3.3)).toDouble());
+ EXPECT_EQ(-3.3, (-MediaTime::createWithDouble(3.3)).toDouble());
+ EXPECT_EQ(3.3 * 2, (MediaTime::createWithDouble(3.3) * 2).toDouble());
+
+ // Floating Point and non-Floating Point math
+ EXPECT_EQ(2.0, (MediaTime::createWithDouble(1.5) + MediaTime(1, 2)).toDouble());
+ EXPECT_EQ(1.0, (MediaTime::createWithDouble(1.5) - MediaTime(1, 2)).toDouble());
+
+ // Overflow Behavior
+ EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 64.0f)), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime::createWithFloat(-pow(2.0f, 64.0f)), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 63.0f), 2).timeScale(), 1);
+ EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 63.0f), 3).timeScale(), 1);
+ EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 64.0)), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime::createWithDouble(-pow(2.0, 64.0)), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 63.0), 2).timeScale(), 1);
+ EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 63.0), 3).timeScale(), 1);
+ EXPECT_EQ((MediaTime(numeric_limits<int64_t>::max(), 2) + MediaTime(numeric_limits<int64_t>::max(), 2)).timeScale(), 1);
+ EXPECT_EQ((MediaTime(numeric_limits<int64_t>::min(), 2) - MediaTime(numeric_limits<int64_t>::max(), 2)).timeScale(), 1);
+ EXPECT_EQ(MediaTime(numeric_limits<int64_t>::max(), 1) + MediaTime(numeric_limits<int64_t>::max(), 1), MediaTime::positiveInfiniteTime());
+ EXPECT_EQ(MediaTime(numeric_limits<int64_t>::min(), 1) + MediaTime(numeric_limits<int64_t>::min(), 1), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime(numeric_limits<int64_t>::min(), 1) - MediaTime(numeric_limits<int64_t>::max(), 1), MediaTime::negativeInfiniteTime());
+ EXPECT_EQ(MediaTime(numeric_limits<int64_t>::max(), 1) - MediaTime(numeric_limits<int64_t>::min(), 1), MediaTime::positiveInfiniteTime());
+}
+
+}
+
diff --git a/Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp b/Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp
new file mode 100644
index 000000000..59af4f849
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp
@@ -0,0 +1,957 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <stdarg.h>
+#include <wtf/MetaAllocator.h>
+#include <wtf/Vector.h>
+
+#if OS(WINDOWS)
+#undef small
+#endif
+
+using namespace WTF;
+
+namespace TestWebKitAPI {
+
+class MetaAllocatorTest: public testing::Test {
+public:
+ enum SanityCheckMode { RunSanityCheck, DontRunSanityCheck };
+
+ enum HeapGrowthMode { DontGrowHeap, ForTestDemandAllocCoalesce, ForTestDemandAllocDontCoalesce };
+
+ HeapGrowthMode currentHeapGrowthMode;
+ size_t allowAllocatePages;
+ size_t requestedNumPages;
+
+ class SimpleTestAllocator: public MetaAllocator {
+ public:
+ SimpleTestAllocator(MetaAllocatorTest* parent)
+ : MetaAllocator(32)
+ , m_parent(parent)
+ {
+ addFreshFreeSpace(reinterpret_cast<void*>(basePage * pageSize()), defaultPagesInHeap * pageSize());
+ }
+
+ virtual ~SimpleTestAllocator()
+ {
+ EXPECT_TRUE(!m_parent->allocatorDestroyed);
+ m_parent->allocatorDestroyed = true;
+ }
+
+ virtual void* allocateNewSpace(size_t& numPages)
+ {
+ switch (m_parent->currentHeapGrowthMode) {
+ case DontGrowHeap:
+ return 0;
+
+ case ForTestDemandAllocCoalesce:
+ case ForTestDemandAllocDontCoalesce: {
+ EXPECT_TRUE(m_parent->allowAllocatePages);
+ EXPECT_TRUE(m_parent->allowAllocatePages >= numPages);
+ m_parent->requestedNumPages = numPages;
+ numPages = m_parent->allowAllocatePages;
+
+ unsigned offset;
+ if (m_parent->currentHeapGrowthMode == ForTestDemandAllocCoalesce)
+ offset = 0;
+ else
+ offset = 1;
+
+ void* result = reinterpret_cast<void*>((basePage + defaultPagesInHeap + offset) * pageSize());
+
+ m_parent->allowAllocatePages = 0;
+ m_parent->currentHeapGrowthMode = DontGrowHeap;
+
+ for (size_t counter = 0; counter < numPages + offset; ++counter) {
+ m_parent->pageMap->append(false);
+ for (unsigned byteCounter = 0; byteCounter < pageSize(); ++byteCounter)
+ m_parent->memoryMap->append(false);
+ }
+
+ m_parent->additionalPagesInHeap += numPages;
+
+ return result;
+ }
+
+ default:
+ CRASH();
+ return 0;
+ }
+ }
+
+ virtual void notifyNeedPage(void* page)
+ {
+ // the page should be both free and unmapped.
+ EXPECT_TRUE(!m_parent->pageState(reinterpret_cast<uintptr_t>(page) / pageSize()));
+ for (uintptr_t address = reinterpret_cast<uintptr_t>(page); address < reinterpret_cast<uintptr_t>(page) + pageSize(); ++address)
+ EXPECT_TRUE(!m_parent->byteState(reinterpret_cast<void*>(address)));
+ m_parent->pageState(reinterpret_cast<uintptr_t>(page) / pageSize()) = true;
+ }
+
+ virtual void notifyPageIsFree(void* page)
+ {
+ // the page should be free of objects at this point, but it should still
+ // be mapped.
+ EXPECT_TRUE(m_parent->pageState(reinterpret_cast<uintptr_t>(page) / pageSize()));
+ for (uintptr_t address = reinterpret_cast<uintptr_t>(page); address < reinterpret_cast<uintptr_t>(page) + pageSize(); ++address)
+ EXPECT_TRUE(!m_parent->byteState(reinterpret_cast<void*>(address)));
+ m_parent->pageState(reinterpret_cast<uintptr_t>(page) / pageSize()) = false;
+ }
+
+ private:
+ MetaAllocatorTest* m_parent;
+ };
+
+ static const unsigned basePage = 1;
+ static const unsigned defaultPagesInHeap = 100;
+
+ unsigned additionalPagesInHeap;
+
+ Vector<bool, 0>* memoryMap;
+ Vector<bool, 0>* pageMap;
+ bool allocatorDestroyed;
+
+ SimpleTestAllocator* allocator;
+
+ virtual void SetUp()
+ {
+ memoryMap = new Vector<bool, 0>();
+ pageMap = new Vector<bool, 0>();
+
+ for (unsigned page = basePage; page < basePage + defaultPagesInHeap; ++page) {
+ pageMap->append(false);
+ for (unsigned byteInPage = 0; byteInPage < pageSize(); ++byteInPage)
+ memoryMap->append(false);
+ }
+
+ allocatorDestroyed = false;
+
+ currentHeapGrowthMode = DontGrowHeap;
+ allowAllocatePages = 0;
+ additionalPagesInHeap = 0;
+ requestedNumPages = 0;
+
+ allocator = new SimpleTestAllocator(this);
+ }
+
+ virtual void TearDown()
+ {
+ EXPECT_TRUE(currentHeapGrowthMode == DontGrowHeap);
+ EXPECT_EQ(allowAllocatePages, static_cast<size_t>(0));
+ EXPECT_EQ(requestedNumPages, static_cast<size_t>(0));
+
+ // memory should be free.
+ for (unsigned page = basePage; page < basePage + defaultPagesInHeap; ++page) {
+ EXPECT_TRUE(!pageState(page));
+ for (unsigned byteInPage = 0; byteInPage < pageSize(); ++byteInPage)
+ EXPECT_TRUE(!byteState(page * pageSize() + byteInPage));
+ }
+
+ // NOTE: this automatically tests that the allocator did not leak
+ // memory, so long as these tests are running with !defined(NDEBUG).
+ // See MetaAllocator::m_mallocBalance.
+ delete allocator;
+
+ EXPECT_TRUE(allocatorDestroyed);
+
+ delete memoryMap;
+ delete pageMap;
+ }
+
+ MetaAllocatorHandle* allocate(size_t sizeInBytes, SanityCheckMode sanityCheckMode = RunSanityCheck)
+ {
+ MetaAllocatorHandle* handle = allocator->allocate(sizeInBytes, 0).leakRef();
+ EXPECT_TRUE(handle);
+ EXPECT_EQ(handle->sizeInBytes(), sizeInBytes);
+
+ uintptr_t startByte = reinterpret_cast<uintptr_t>(handle->start());
+ uintptr_t endByte = startByte + sizeInBytes;
+ for (uintptr_t currentByte = startByte; currentByte < endByte; ++currentByte) {
+ EXPECT_TRUE(!byteState(currentByte));
+ byteState(currentByte) = true;
+ EXPECT_TRUE(pageState(currentByte / pageSize()));
+ }
+
+ if (sanityCheckMode == RunSanityCheck)
+ sanityCheck();
+
+ return handle;
+ }
+
+ void free(MetaAllocatorHandle* handle, SanityCheckMode sanityCheckMode = RunSanityCheck)
+ {
+ EXPECT_TRUE(handle);
+
+ notifyFree(handle->start(), handle->sizeInBytes());
+ handle->deref();
+
+ if (sanityCheckMode == RunSanityCheck)
+ sanityCheck();
+ }
+
+ void notifyFree(void* start, size_t sizeInBytes)
+ {
+ uintptr_t startByte = reinterpret_cast<uintptr_t>(start);
+ uintptr_t endByte = startByte + sizeInBytes;
+ for (uintptr_t currentByte = startByte; currentByte < endByte; ++currentByte) {
+ EXPECT_TRUE(byteState(currentByte));
+ byteState(currentByte) = false;
+ }
+ }
+
+ void sanityCheck()
+ {
+#ifndef NDEBUG
+ EXPECT_EQ(allocator->bytesReserved() - allocator->bytesAllocated(), allocator->debugFreeSpaceSize());
+#endif
+ EXPECT_EQ(allocator->bytesReserved(), (defaultPagesInHeap + additionalPagesInHeap) * pageSize());
+ EXPECT_EQ(allocator->bytesAllocated(), bytesAllocated());
+ EXPECT_EQ(allocator->bytesCommitted(), bytesCommitted());
+ }
+
+ void confirm(MetaAllocatorHandle* handle)
+ {
+ uintptr_t startByte = reinterpret_cast<uintptr_t>(handle->start());
+ confirm(startByte, startByte + handle->sizeInBytes(), true);
+ }
+
+ void confirmHighWatermark(MetaAllocatorHandle* handle)
+ {
+ confirm(reinterpret_cast<uintptr_t>(handle->end()), (basePage + defaultPagesInHeap) * pageSize(), false);
+ }
+
+ void confirm(uintptr_t startByte, uintptr_t endByte, bool value)
+ {
+ for (uintptr_t currentByte = startByte; currentByte < endByte; ++currentByte) {
+ EXPECT_EQ(byteState(currentByte), value);
+ if (value)
+ EXPECT_TRUE(pageState(currentByte / pageSize()));
+ }
+ if (!value) {
+ uintptr_t firstFreePage = (startByte + pageSize() - 1) / pageSize();
+ uintptr_t lastFreePage = (endByte - pageSize()) / pageSize();
+ for (uintptr_t currentPage = firstFreePage; currentPage <= lastFreePage; ++currentPage)
+ EXPECT_TRUE(!pageState(currentPage));
+ }
+ }
+
+ size_t bytesAllocated()
+ {
+ size_t result = 0;
+ for (unsigned index = 0; index < memoryMap->size(); ++index) {
+ if (memoryMap->at(index))
+ result++;
+ }
+ return result;
+ }
+
+ size_t bytesCommitted()
+ {
+ size_t result = 0;
+ for (unsigned index = 0; index < pageMap->size(); ++index) {
+ if (pageMap->at(index))
+ result++;
+ }
+ return result * pageSize();
+ }
+
+ bool& byteState(void* address)
+ {
+ return byteState(reinterpret_cast<uintptr_t>(address));
+ }
+
+ bool& byteState(uintptr_t address)
+ {
+ uintptr_t byteIndex = address - basePage * pageSize();
+ return memoryMap->at(byteIndex);
+ }
+
+ bool& pageState(uintptr_t page)
+ {
+ uintptr_t pageIndex = page - basePage;
+ return pageMap->at(pageIndex);
+ }
+
+ // Test helpers
+
+ void testOneAlloc(size_t size)
+ {
+ // Tests the most basic behavior: allocate one thing and free it. Also
+ // verifies that the state of pages is correct.
+
+ MetaAllocatorHandle* handle = allocate(size);
+ EXPECT_EQ(handle->start(), reinterpret_cast<void*>(basePage * pageSize()));
+ EXPECT_EQ(handle->sizeInBytes(), size);
+ EXPECT_TRUE(pageState(basePage));
+
+ confirm(handle);
+ confirmHighWatermark(handle);
+
+ free(handle);
+ }
+
+ void testRepeatAllocFree(size_t firstSize, ...)
+ {
+ // Tests right-coalescing by repeatedly allocating and freeing. The idea
+ // is that if you allocate something and then free it, then the heap should
+ // look identical to what it was before the allocation due to a right-coalesce
+ // of the freed chunk and the already-free memory, and so subsequent
+ // allocations should behave the same as the first one.
+
+ MetaAllocatorHandle* handle = allocate(firstSize);
+ EXPECT_EQ(handle->start(), reinterpret_cast<void*>(basePage * pageSize()));
+ EXPECT_EQ(handle->sizeInBytes(), firstSize);
+
+ confirm(handle);
+ confirmHighWatermark(handle);
+
+ free(handle);
+
+ va_list argList;
+ va_start(argList, firstSize);
+ while (size_t sizeInBytes = va_arg(argList, int)) {
+ handle = allocate(sizeInBytes);
+ EXPECT_EQ(handle->start(), reinterpret_cast<void*>(basePage * pageSize()));
+ EXPECT_EQ(handle->sizeInBytes(), sizeInBytes);
+
+ confirm(handle);
+ confirmHighWatermark(handle);
+
+ free(handle);
+ }
+ va_end(argList);
+ }
+
+ void testSimpleFullCoalesce(size_t firstSize, size_t secondSize, size_t thirdSize)
+ {
+ // Allocates something of size firstSize, then something of size secondSize, and then
+ // frees the first allocation, and then the second, and then attempts to allocate the
+ // third, asserting that it allocated at the base address of the heap.
+
+ // Note that this test may cause right-allocation, which will cause the test to fail.
+ // Thus the correct way of running this test is to ensure that secondSize is
+ // picked in such a way that it never straddles a page.
+
+ MetaAllocatorHandle* firstHandle = allocate(firstSize);
+ EXPECT_EQ(firstHandle->start(), reinterpret_cast<void*>(basePage * pageSize()));
+ EXPECT_EQ(firstHandle->sizeInBytes(), firstSize);
+
+ confirm(firstHandle);
+ confirmHighWatermark(firstHandle);
+
+ MetaAllocatorHandle* secondHandle = allocate(secondSize);
+ EXPECT_EQ(secondHandle->start(), reinterpret_cast<void*>(basePage * pageSize() + firstSize));
+ EXPECT_EQ(secondHandle->sizeInBytes(), secondSize);
+
+ confirm(firstHandle);
+ confirm(secondHandle);
+ confirmHighWatermark(secondHandle);
+
+ free(firstHandle);
+
+ confirm(secondHandle);
+ confirmHighWatermark(secondHandle);
+
+ free(secondHandle);
+
+ confirm(basePage * pageSize(), (basePage + defaultPagesInHeap) * pageSize(), false);
+
+ MetaAllocatorHandle* thirdHandle = allocate(thirdSize);
+ EXPECT_EQ(thirdHandle->start(), reinterpret_cast<void*>(basePage * pageSize()));
+ EXPECT_EQ(thirdHandle->sizeInBytes(), thirdSize);
+
+ confirm(thirdHandle);
+ confirmHighWatermark(thirdHandle);
+
+ free(thirdHandle);
+ }
+
+ enum TestFIFOAllocMode { FillAtEnd, EagerFill };
+ void testFIFOAlloc(TestFIFOAllocMode mode, ...)
+ {
+ // This will test the simple case of no-coalesce (freeing the left-most
+ // chunk in memory when the chunk to the right of it is allocated) and
+ // fully exercise left-coalescing and full-coalescing. In EagerFill
+ // mode, this also tests perfect-fit allocation and no-coalescing free.
+
+ size_t totalSize = 0;
+
+ Vector<MetaAllocatorHandle*, 0> handles;
+
+ va_list argList;
+ va_start(argList, mode);
+ while (size_t sizeInBytes = va_arg(argList, int)) {
+ MetaAllocatorHandle* handle = allocate(sizeInBytes);
+ EXPECT_EQ(handle->start(), reinterpret_cast<void*>(basePage * pageSize() + totalSize));
+ EXPECT_EQ(handle->sizeInBytes(), sizeInBytes);
+
+ confirm(handle);
+ confirmHighWatermark(handle);
+
+ handles.append(handle);
+ totalSize += sizeInBytes;
+ }
+ va_end(argList);
+
+ for (unsigned index = 0; index < handles.size(); ++index)
+ confirm(handles.at(index));
+
+ size_t sizeSoFar = 0;
+ for (unsigned index = 0; index < handles.size(); ++index) {
+ sizeSoFar += handles.at(index)->sizeInBytes();
+ free(handles.at(index));
+ if (mode == EagerFill) {
+ MetaAllocatorHandle* handle = allocate(sizeSoFar);
+ EXPECT_EQ(handle->start(), reinterpret_cast<void*>(basePage * pageSize()));
+ EXPECT_EQ(handle->sizeInBytes(), sizeSoFar);
+
+ confirm(basePage * pageSize(), basePage * pageSize() + totalSize, true);
+ if (index < handles.size() - 1)
+ confirmHighWatermark(handles.last());
+ else
+ confirmHighWatermark(handle);
+
+ free(handle);
+
+ confirm(basePage * pageSize(), basePage * pageSize() + sizeSoFar, false);
+ }
+ }
+
+ ASSERT(sizeSoFar == totalSize);
+
+ confirm(basePage * pageSize(), (basePage + defaultPagesInHeap) * pageSize(), false);
+
+ if (mode == FillAtEnd) {
+ MetaAllocatorHandle* finalHandle = allocate(totalSize);
+ EXPECT_EQ(finalHandle->start(), reinterpret_cast<void*>(basePage * pageSize()));
+ EXPECT_EQ(finalHandle->sizeInBytes(), totalSize);
+
+ confirm(finalHandle);
+ confirmHighWatermark(finalHandle);
+
+ free(finalHandle);
+ }
+ }
+
+ void testFillHeap(size_t sizeInBytes, size_t numAllocations)
+ {
+ Vector<MetaAllocatorHandle*, 0> handles;
+
+ for (size_t index = 0; index < numAllocations; ++index)
+ handles.append(allocate(sizeInBytes, DontRunSanityCheck));
+
+ sanityCheck();
+
+ EXPECT_TRUE(!allocator->allocate(sizeInBytes, 0));
+
+ for (size_t index = 0; index < numAllocations; ++index)
+ free(handles.at(index), DontRunSanityCheck);
+
+ sanityCheck();
+ }
+
+ void testRightAllocation(size_t firstLeftSize, size_t firstRightSize, size_t secondLeftSize, size_t secondRightSize)
+ {
+ MetaAllocatorHandle* firstLeft = allocate(firstLeftSize);
+ EXPECT_EQ(firstLeft->start(), reinterpret_cast<void*>(basePage * pageSize()));
+
+ MetaAllocatorHandle* firstRight = allocate(firstRightSize);
+ EXPECT_EQ(firstRight->end(), reinterpret_cast<void*>((basePage + defaultPagesInHeap) * pageSize()));
+
+ MetaAllocatorHandle* secondLeft = allocate(secondLeftSize);
+ EXPECT_EQ(secondLeft->start(), reinterpret_cast<void*>(basePage * pageSize() + firstLeft->sizeInBytes()));
+
+ MetaAllocatorHandle* secondRight = allocate(secondRightSize);
+ EXPECT_EQ(secondRight->end(), reinterpret_cast<void*>((basePage + defaultPagesInHeap) * pageSize() - firstRight->sizeInBytes()));
+
+ free(firstLeft);
+ free(firstRight);
+ free(secondLeft);
+ free(secondRight);
+
+ MetaAllocatorHandle* final = allocate(defaultPagesInHeap * pageSize());
+ EXPECT_EQ(final->start(), reinterpret_cast<void*>(basePage * pageSize()));
+
+ free(final);
+ }
+
+ void testBestFit(size_t firstSize, size_t step, unsigned numSlots, SanityCheckMode sanityCheckMode)
+ {
+ Vector<MetaAllocatorHandle*, 0> handlesToFree;
+ Vector<MetaAllocatorHandle*, 0> handles;
+ Vector<void*, 0> locations;
+
+ size_t size = firstSize;
+ for (unsigned index = 0; index < numSlots; ++index) {
+ MetaAllocatorHandle* toFree = allocate(size, sanityCheckMode);
+ if (!handles.isEmpty()) {
+ while (toFree->start() != handles.last()->end()) {
+ handlesToFree.append(toFree);
+ toFree = allocate(size, sanityCheckMode);
+ }
+ }
+
+ MetaAllocatorHandle* fragger = allocate(32, sanityCheckMode);
+ EXPECT_EQ(fragger->start(), toFree->end());
+
+ locations.append(toFree->start());
+
+ handlesToFree.append(toFree);
+ handles.append(fragger);
+
+ size += step;
+ }
+
+ ASSERT(locations.size() == numSlots);
+
+ for (unsigned index = 0; index < handlesToFree.size(); ++index)
+ free(handlesToFree.at(index), sanityCheckMode);
+
+ size = firstSize;
+ for (unsigned index = 0; index < numSlots; ++index) {
+ MetaAllocatorHandle* bestFit = allocate(size - 32, sanityCheckMode);
+
+ EXPECT_TRUE(bestFit->start() == locations.at(index)
+ || bestFit->end() == reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(locations.at(index)) + size));
+
+ MetaAllocatorHandle* small = allocate(32, sanityCheckMode);
+ if (bestFit->start() == locations.at(index))
+ EXPECT_EQ(small->start(), bestFit->end());
+ else
+ EXPECT_EQ(small->end(), bestFit->start());
+
+ free(bestFit, sanityCheckMode);
+ free(small, sanityCheckMode);
+
+ size += step;
+ }
+
+ for (unsigned index = 0; index < numSlots; ++index)
+ free(handles.at(index), sanityCheckMode);
+
+ MetaAllocatorHandle* final = allocate(defaultPagesInHeap * pageSize(), sanityCheckMode);
+ EXPECT_EQ(final->start(), reinterpret_cast<void*>(basePage * pageSize()));
+
+ free(final, sanityCheckMode);
+ }
+
+ void testShrink(size_t firstSize, size_t secondSize)
+ {
+ // Allocate the thing that will be shrunk
+ MetaAllocatorHandle* handle = allocate(firstSize);
+
+ // Shrink it, and make sure that our state reflects the shrinkage.
+ notifyFree(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(handle->start()) + secondSize), firstSize - secondSize);
+
+ handle->shrink(secondSize);
+ EXPECT_EQ(handle->sizeInBytes(), secondSize);
+
+ sanityCheck();
+
+ // Assert that the heap is not empty.
+ EXPECT_TRUE(!allocator->allocate(defaultPagesInHeap * pageSize(), 0));
+
+ // Allocate the remainder of the heap.
+ MetaAllocatorHandle* remainder = allocate(defaultPagesInHeap * pageSize() - secondSize);
+ EXPECT_EQ(remainder->start(), handle->end());
+
+ free(remainder);
+ free(handle);
+
+ // Assert that the heap is empty and finish up.
+ MetaAllocatorHandle* final = allocate(defaultPagesInHeap * pageSize());
+ EXPECT_EQ(final->start(), reinterpret_cast<void*>(basePage * pageSize()));
+
+ free(final);
+ }
+
+ void testDemandAllocCoalesce(size_t firstSize, size_t numPages, size_t secondSize)
+ {
+ EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0));
+
+ MetaAllocatorHandle* firstHandle = allocate(firstSize);
+
+ EXPECT_TRUE(!allocator->allocate(secondSize, 0));
+ EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0));
+
+ currentHeapGrowthMode = ForTestDemandAllocCoalesce;
+ allowAllocatePages = numPages;
+
+ MetaAllocatorHandle* secondHandle = allocate(secondSize);
+
+ EXPECT_TRUE(currentHeapGrowthMode == DontGrowHeap);
+ EXPECT_EQ(allowAllocatePages, static_cast<size_t>(0));
+ EXPECT_EQ(requestedNumPages, (secondSize + pageSize() - 1) / pageSize());
+ EXPECT_EQ(secondHandle->start(), reinterpret_cast<void*>((basePage + defaultPagesInHeap) * pageSize()));
+
+ requestedNumPages = 0;
+
+ free(firstHandle);
+ free(secondHandle);
+
+ free(allocate((defaultPagesInHeap + numPages) * pageSize()));
+ }
+
+ void testDemandAllocDontCoalesce(size_t firstSize, size_t numPages, size_t secondSize)
+ {
+ free(allocate(firstSize));
+ free(allocate(defaultPagesInHeap * pageSize()));
+ EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0));
+
+ MetaAllocatorHandle* firstHandle = allocate(firstSize);
+
+ EXPECT_TRUE(!allocator->allocate(secondSize, 0));
+ EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0));
+
+ currentHeapGrowthMode = ForTestDemandAllocDontCoalesce;
+ allowAllocatePages = numPages;
+
+ MetaAllocatorHandle* secondHandle = allocate(secondSize);
+
+ EXPECT_TRUE(currentHeapGrowthMode == DontGrowHeap);
+ EXPECT_EQ(allowAllocatePages, static_cast<size_t>(0));
+ EXPECT_EQ(requestedNumPages, (secondSize + pageSize() - 1) / pageSize());
+ EXPECT_EQ(secondHandle->start(), reinterpret_cast<void*>((basePage + defaultPagesInHeap + 1) * pageSize()));
+
+ requestedNumPages = 0;
+
+ EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0));
+
+ free(firstHandle);
+ free(secondHandle);
+
+ EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0));
+
+ firstHandle = allocate(firstSize);
+ secondHandle = allocate(secondSize);
+ EXPECT_EQ(firstHandle->start(), reinterpret_cast<void*>(basePage * pageSize()));
+ EXPECT_EQ(secondHandle->start(), reinterpret_cast<void*>((basePage + defaultPagesInHeap + 1) * pageSize()));
+ free(firstHandle);
+ free(secondHandle);
+ }
+};
+
+TEST_F(MetaAllocatorTest, Empty)
+{
+ // Tests that creating and destroying an allocator works.
+}
+
+TEST_F(MetaAllocatorTest, AllocZero)
+{
+ // Tests that allocating a zero-length block returns 0 and
+ // does not change anything in memory.
+
+ ASSERT(!allocator->allocate(0, 0));
+
+ MetaAllocatorHandle* final = allocate(defaultPagesInHeap * pageSize());
+ EXPECT_EQ(final->start(), reinterpret_cast<void*>(basePage * pageSize()));
+ free(final);
+}
+
+TEST_F(MetaAllocatorTest, OneAlloc32)
+{
+ testOneAlloc(32);
+}
+
+TEST_F(MetaAllocatorTest, OneAlloc64)
+{
+ testOneAlloc(64);
+}
+
+TEST_F(MetaAllocatorTest, OneAllocTwoPages)
+{
+ testOneAlloc(pageSize() * 2);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFree32Twice)
+{
+ testRepeatAllocFree(32, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFree32Then64)
+{
+ testRepeatAllocFree(32, 64, 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFree64Then32)
+{
+ testRepeatAllocFree(64, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFree32TwiceThen64)
+{
+ testRepeatAllocFree(32, 32, 64, 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFree32Then64Twice)
+{
+ testRepeatAllocFree(32, 64, 64, 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFree64Then32Then64)
+{
+ testRepeatAllocFree(64, 32, 64, 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFree32Thrice)
+{
+ testRepeatAllocFree(32, 32, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFree32Then64Then32)
+{
+ testRepeatAllocFree(32, 32, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFree64Then32Twice)
+{
+ testRepeatAllocFree(64, 32, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFreeTwoPagesThen32)
+{
+ testRepeatAllocFree(static_cast<int>(pageSize() * 2), 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFree32ThenTwoPages)
+{
+ testRepeatAllocFree(32, static_cast<int>(pageSize() * 2), 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFreePageThenTwoPages)
+{
+ testRepeatAllocFree(static_cast<int>(pageSize()), static_cast<int>(pageSize() * 2), 0);
+}
+
+TEST_F(MetaAllocatorTest, RepeatAllocFreeTwoPagesThenPage)
+{
+ testRepeatAllocFree(static_cast<int>(pageSize() * 2), static_cast<int>(pageSize()), 0);
+}
+
+TEST_F(MetaAllocatorTest, SimpleFullCoalesce32Plus32Then128)
+{
+ testSimpleFullCoalesce(32, 32, 128);
+}
+
+TEST_F(MetaAllocatorTest, SimpleFullCoalesce32Plus64Then128)
+{
+ testSimpleFullCoalesce(32, 64, 128);
+}
+
+TEST_F(MetaAllocatorTest, SimpleFullCoalesce64Plus32Then128)
+{
+ testSimpleFullCoalesce(64, 32, 128);
+}
+
+TEST_F(MetaAllocatorTest, SimpleFullCoalesce32PlusPageLess32ThenPage)
+{
+ testSimpleFullCoalesce(32, pageSize() - 32, pageSize());
+}
+
+TEST_F(MetaAllocatorTest, SimpleFullCoalesce32PlusPageLess32ThenTwoPages)
+{
+ testSimpleFullCoalesce(32, pageSize() - 32, pageSize() * 2);
+}
+
+TEST_F(MetaAllocatorTest, SimpleFullCoalescePagePlus32ThenTwoPages)
+{
+ testSimpleFullCoalesce(pageSize(), 32, pageSize() * 2);
+}
+
+TEST_F(MetaAllocatorTest, SimpleFullCoalescePagePlusPageThenTwoPages)
+{
+ testSimpleFullCoalesce(pageSize(), pageSize(), pageSize() * 2);
+}
+
+TEST_F(MetaAllocatorTest, FIFOAllocFillAtEnd32Twice)
+{
+ testFIFOAlloc(FillAtEnd, 32, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, FIFOAllocFillAtEnd32Thrice)
+{
+ testFIFOAlloc(FillAtEnd, 32, 32, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, FIFOAllocFillAtEnd32FourTimes)
+{
+ testFIFOAlloc(FillAtEnd, 32, 32, 32, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, FIFOAllocFillAtEndPageLess32Then32ThenPageLess64Then64)
+{
+ testFIFOAlloc(FillAtEnd, static_cast<int>(pageSize() - 32), 32, static_cast<int>(pageSize() - 64), 64, 0);
+}
+
+TEST_F(MetaAllocatorTest, FIFOAllocEagerFill32Twice)
+{
+ testFIFOAlloc(EagerFill, 32, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, FIFOAllocEagerFill32Thrice)
+{
+ testFIFOAlloc(EagerFill, 32, 32, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, FIFOAllocEagerFill32FourTimes)
+{
+ testFIFOAlloc(EagerFill, 32, 32, 32, 32, 0);
+}
+
+TEST_F(MetaAllocatorTest, FIFOAllocEagerFillPageLess32Then32ThenPageLess64Then64)
+{
+ testFIFOAlloc(EagerFill, static_cast<int>(pageSize() - 32), 32, static_cast<int>(pageSize() - 64), 64, 0);
+}
+
+TEST_F(MetaAllocatorTest, FillHeap32)
+{
+ testFillHeap(32, defaultPagesInHeap * pageSize() / 32);
+}
+
+TEST_F(MetaAllocatorTest, FillHeapPage)
+{
+ testFillHeap(pageSize(), defaultPagesInHeap);
+}
+
+TEST_F(MetaAllocatorTest, FillHeapTwoPages)
+{
+ testFillHeap(pageSize() * 2, defaultPagesInHeap / 2);
+}
+
+TEST_F(MetaAllocatorTest, RightAllocation32ThenPageThen32ThenPage)
+{
+ testRightAllocation(32, pageSize(), 32, pageSize());
+}
+
+TEST_F(MetaAllocatorTest, RightAllocationQuarterPageThenPageThenQuarterPageThenPage)
+{
+ testRightAllocation(pageSize() / 4, pageSize(), pageSize() / 4, pageSize());
+}
+
+TEST_F(MetaAllocatorTest, BestFit64Plus64Thrice)
+{
+ testBestFit(64, 64, 3, RunSanityCheck);
+}
+
+TEST_F(MetaAllocatorTest, BestFit64Plus64TenTimes)
+{
+ testBestFit(64, 64, 10, DontRunSanityCheck);
+}
+
+TEST_F(MetaAllocatorTest, BestFit64Plus64HundredTimes)
+{
+ testBestFit(64, 64, 100, DontRunSanityCheck);
+}
+
+TEST_F(MetaAllocatorTest, BestFit96Plus64Thrice)
+{
+ testBestFit(96, 64, 3, RunSanityCheck);
+}
+
+TEST_F(MetaAllocatorTest, BestFit96Plus64TenTimes)
+{
+ testBestFit(96, 64, 10, DontRunSanityCheck);
+}
+
+TEST_F(MetaAllocatorTest, BestFit96Plus64HundredTimes)
+{
+ testBestFit(96, 64, 100, DontRunSanityCheck);
+}
+
+TEST_F(MetaAllocatorTest, BestFit96Plus96Thrice)
+{
+ testBestFit(96, 96, 3, RunSanityCheck);
+}
+
+TEST_F(MetaAllocatorTest, BestFit96Plus96TenTimes)
+{
+ testBestFit(96, 96, 10, DontRunSanityCheck);
+}
+
+TEST_F(MetaAllocatorTest, BestFit96Plus96EightyTimes)
+{
+ testBestFit(96, 96, 80, DontRunSanityCheck);
+}
+
+TEST_F(MetaAllocatorTest, Shrink64To32)
+{
+ testShrink(64, 32);
+}
+
+TEST_F(MetaAllocatorTest, ShrinkPageTo32)
+{
+ testShrink(pageSize(), 32);
+}
+
+TEST_F(MetaAllocatorTest, ShrinkPageToPageLess32)
+{
+ testShrink(pageSize(), pageSize() - 32);
+}
+
+TEST_F(MetaAllocatorTest, ShrinkTwoPagesTo32)
+{
+ testShrink(pageSize() * 2, 32);
+}
+
+TEST_F(MetaAllocatorTest, ShrinkTwoPagesToPagePlus32)
+{
+ testShrink(pageSize() * 2, pageSize() + 32);
+}
+
+TEST_F(MetaAllocatorTest, ShrinkTwoPagesToPage)
+{
+ testShrink(pageSize() * 2, pageSize());
+}
+
+TEST_F(MetaAllocatorTest, ShrinkTwoPagesToPageLess32)
+{
+ testShrink(pageSize() * 2, pageSize() - 32);
+}
+
+TEST_F(MetaAllocatorTest, ShrinkTwoPagesToTwoPagesLess32)
+{
+ testShrink(pageSize() * 2, pageSize() * 2 - 32);
+}
+
+TEST_F(MetaAllocatorTest, DemandAllocCoalescePageThenDoubleHeap)
+{
+ testDemandAllocCoalesce(pageSize(), defaultPagesInHeap, defaultPagesInHeap * pageSize());
+}
+
+TEST_F(MetaAllocatorTest, DemandAllocCoalescePageThenTripleHeap)
+{
+ testDemandAllocCoalesce(pageSize(), defaultPagesInHeap * 2, defaultPagesInHeap * pageSize());
+}
+
+TEST_F(MetaAllocatorTest, DemandAllocDontCoalescePageThenDoubleHeap)
+{
+ testDemandAllocDontCoalesce(pageSize(), defaultPagesInHeap, defaultPagesInHeap * pageSize());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/MoveOnly.h b/Tools/TestWebKitAPI/Tests/WTF/MoveOnly.h
new file mode 100644
index 000000000..cecc49152
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/MoveOnly.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MoveOnly_h
+#define MoveOnly_h
+
+#include <wtf/HashFunctions.h>
+#include <wtf/HashTraits.h>
+
+class MoveOnly {
+public:
+ MoveOnly()
+ : m_value(0)
+ {
+ }
+
+ MoveOnly(unsigned value)
+ : m_value(value)
+ {
+ }
+
+ unsigned value() const
+ {
+ return m_value;
+ }
+
+ MoveOnly(MoveOnly&& other)
+ : m_value(other.m_value)
+ {
+ other.m_value = 0;
+ }
+
+ MoveOnly& operator=(MoveOnly&& other)
+ {
+ if (this == &other)
+ return *this;
+
+ m_value = other.m_value;
+ other.m_value = 0;
+ return *this;
+ }
+
+ friend bool operator==(const MoveOnly& a, const MoveOnly& b)
+ {
+ return a.m_value == b.m_value;
+ }
+
+private:
+ unsigned m_value;
+};
+
+namespace WTF {
+
+template<> struct HashTraits<MoveOnly> : public GenericHashTraits<MoveOnly> {
+ static void constructDeletedValue(MoveOnly& slot) { slot = MoveOnly(std::numeric_limits<unsigned>::max()); }
+ static bool isDeletedValue(const MoveOnly& slot) { return slot.value() == std::numeric_limits<unsigned>::max(); }
+};
+
+template<> struct DefaultHash<MoveOnly> {
+ struct Hash {
+ static unsigned hash(const MoveOnly& key)
+ {
+ return intHash(key.value());
+ }
+
+ static bool equal(const MoveOnly& a, const MoveOnly& b)
+ {
+ return a == b;
+ }
+
+ static const bool safeToCompareToEmptyOrDeleted = true;
+ };
+};
+} // namespace WTF
+
+#endif // MoveOnly_h
diff --git a/Tools/TestWebKitAPI/Tests/WTF/NakedPtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/NakedPtr.cpp
new file mode 100644
index 000000000..414507f15
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/NakedPtr.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "RefLogger.h"
+#include <wtf/NakedPtr.h>
+
+namespace TestWebKitAPI {
+
+// For these tests, we need a base class and a derived class. For this purpose,
+// we reuse the RefLogger and DerivedRefLogger classes.
+
+TEST(WTF_NakedPtr, Basic)
+{
+ DerivedRefLogger a("a");
+
+ NakedPtr<RefLogger> empty;
+ ASSERT_EQ(nullptr, empty.get());
+
+ {
+ NakedPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ASSERT_EQ(&a, &*ptr);
+ ASSERT_EQ(&a.name, &ptr->name);
+ }
+
+ {
+ NakedPtr<RefLogger> ptr = &a;
+ ASSERT_EQ(&a, ptr.get());
+ }
+
+ {
+ NakedPtr<RefLogger> p1 = &a;
+ NakedPtr<RefLogger> p2(p1);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+
+ {
+ NakedPtr<RefLogger> p1 = &a;
+ NakedPtr<RefLogger> p2 = p1;
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+
+ {
+ NakedPtr<RefLogger> p1 = &a;
+ NakedPtr<RefLogger> p2 = WTF::move(p1);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+
+ {
+ NakedPtr<RefLogger> p1 = &a;
+ NakedPtr<RefLogger> p2(WTF::move(p1));
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+
+ {
+ NakedPtr<DerivedRefLogger> p1 = &a;
+ NakedPtr<RefLogger> p2 = p1;
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+
+ {
+ NakedPtr<DerivedRefLogger> p1 = &a;
+ NakedPtr<RefLogger> p2 = WTF::move(p1);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+
+ {
+ NakedPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ptr.clear();
+ ASSERT_EQ(nullptr, ptr.get());
+ }
+}
+
+TEST(WTF_NakedPtr, Assignment)
+{
+ DerivedRefLogger a("a");
+ RefLogger b("b");
+ DerivedRefLogger c("c");
+
+ {
+ NakedPtr<RefLogger> p1(&a);
+ NakedPtr<RefLogger> p2(&b);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ p1 = p2;
+ ASSERT_EQ(&b, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ }
+
+ {
+ NakedPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ptr = &b;
+ ASSERT_EQ(&b, ptr.get());
+ }
+
+ {
+ NakedPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ptr = nullptr;
+ ASSERT_EQ(nullptr, ptr.get());
+ }
+
+ {
+ NakedPtr<RefLogger> p1(&a);
+ NakedPtr<RefLogger> p2(&b);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ p1 = WTF::move(p2);
+ ASSERT_EQ(&b, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ }
+
+ {
+ NakedPtr<RefLogger> p1(&a);
+ NakedPtr<DerivedRefLogger> p2(&c);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&c, p2.get());
+ p1 = p2;
+ ASSERT_EQ(&c, p1.get());
+ ASSERT_EQ(&c, p2.get());
+ }
+
+ {
+ NakedPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ptr = &c;
+ ASSERT_EQ(&c, ptr.get());
+ }
+
+ {
+ NakedPtr<RefLogger> p1(&a);
+ NakedPtr<DerivedRefLogger> p2(&c);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&c, p2.get());
+ p1 = WTF::move(p2);
+ ASSERT_EQ(&c, p1.get());
+ ASSERT_EQ(&c, p2.get());
+ }
+
+ {
+ NakedPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ptr = ptr;
+ ASSERT_EQ(&a, ptr.get());
+ }
+
+ {
+ NakedPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ptr = WTF::move(ptr);
+ ASSERT_EQ(&a, ptr.get());
+ }
+}
+
+TEST(WTF_NakedPtr, Swap)
+{
+ RefLogger a("a");
+ RefLogger b("b");
+
+ {
+ NakedPtr<RefLogger> p1(&a);
+ NakedPtr<RefLogger> p2(&b);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ p1.swap(p2);
+ ASSERT_EQ(&b, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+
+ {
+ NakedPtr<RefLogger> p1(&a);
+ NakedPtr<RefLogger> p2(&b);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ std::swap(p1, p2);
+ ASSERT_EQ(&b, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+}
+
+NakedPtr<RefLogger> nakedPtrFoo(RefLogger& logger)
+{
+ return NakedPtr<RefLogger>(&logger);
+}
+
+TEST(WTF_NakedPtr, ReturnValue)
+{
+ DerivedRefLogger a("a");
+
+ {
+ auto ptr = nakedPtrFoo(a);
+ ASSERT_EQ(&a, ptr.get());
+ ASSERT_EQ(&a, &*ptr);
+ ASSERT_EQ(&a.name, &ptr->name);
+ }
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/Optional.cpp b/Tools/TestWebKitAPI/Tests/WTF/Optional.cpp
new file mode 100644
index 000000000..05f413b97
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/Optional.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/Optional.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF_Optional, Disengaged)
+{
+ {
+ Optional<int> optional;
+
+ EXPECT_FALSE(static_cast<bool>(optional));
+ }
+
+ {
+ Optional<int> optional { Nullopt };
+
+ EXPECT_FALSE(static_cast<bool>(optional));
+ }
+}
+
+TEST(WTF_Optional, Engaged)
+{
+ Optional<int> optional { 10 };
+
+ EXPECT_TRUE(static_cast<bool>(optional));
+ EXPECT_EQ(10, optional.value());
+}
+
+TEST(WTF_Optional, Destructor)
+{
+ static bool didCallDestructor = false;
+ struct A {
+ ~A()
+ {
+ EXPECT_FALSE(didCallDestructor);
+ didCallDestructor = true;
+ }
+ };
+
+ {
+ Optional<A> optional { InPlace };
+
+ EXPECT_TRUE(static_cast<bool>(optional));
+ }
+
+ EXPECT_TRUE(didCallDestructor);
+}
+
+TEST(WTF_Optional, Callback)
+{
+ bool called = false;
+ Optional<int> a;
+ int result = a.valueOrCompute([&] {
+ called = true;
+ return 300;
+ });
+ EXPECT_TRUE(called);
+ EXPECT_EQ(result, 300);
+
+ a = 250;
+ called = false;
+ result = a.valueOrCompute([&] {
+ called = true;
+ return 300;
+ });
+ EXPECT_FALSE(called);
+ EXPECT_EQ(result, 250);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/ParkingLot.cpp b/Tools/TestWebKitAPI/Tests/WTF/ParkingLot.cpp
new file mode 100644
index 000000000..cad99d519
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/ParkingLot.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+#include <wtf/DataLog.h>
+#include <wtf/HashSet.h>
+#include <wtf/ListDump.h>
+#include <wtf/ParkingLot.h>
+#include <wtf/Threading.h>
+#include <wtf/ThreadingPrimitives.h>
+
+using namespace WTF;
+
+namespace TestWebKitAPI {
+
+namespace {
+
+struct SingleLatchTest {
+ void initialize(unsigned numThreads)
+ {
+ // This implements a fair (FIFO) semaphore, and it starts out unavailable.
+ semaphore.store(0);
+
+ for (unsigned i = numThreads; i--;) {
+ threads.append(
+ createThread(
+ "Parking Test Thread",
+ [&] () {
+ EXPECT_NE(0u, currentThread());
+
+ down();
+
+ std::lock_guard<std::mutex> locker(lock);
+ awake.add(currentThread());
+ lastAwoken = currentThread();
+ condition.notify_one();
+ }));
+ }
+ }
+
+ void unparkOne(unsigned singleUnparkIndex)
+ {
+ EXPECT_EQ(0u, lastAwoken);
+
+ unsigned numWaitingOnAddress = 0;
+ Vector<ThreadIdentifier, 8> queue;
+ ParkingLot::forEach(
+ [&] (ThreadIdentifier threadIdentifier, const void* address) {
+ if (address != &semaphore)
+ return;
+
+ queue.append(threadIdentifier);
+
+ numWaitingOnAddress++;
+ });
+
+ EXPECT_LE(numWaitingOnAddress, threads.size() - singleUnparkIndex);
+
+ up();
+
+ {
+ std::unique_lock<std::mutex> locker(lock);
+ while (awake.size() < singleUnparkIndex + 1)
+ condition.wait(locker);
+ EXPECT_NE(0u, lastAwoken);
+ if (!queue.isEmpty() && queue[0] != lastAwoken) {
+ dataLog("Woke up wrong thread: queue = ", listDump(queue), ", last awoken = ", lastAwoken, "\n");
+ EXPECT_EQ(queue[0], lastAwoken);
+ }
+ lastAwoken = 0;
+ }
+ }
+
+ void finish(unsigned numSingleUnparks)
+ {
+ unsigned numWaitingOnAddress = 0;
+ ParkingLot::forEach(
+ [&] (ThreadIdentifier, const void* address) {
+ if (address != &semaphore)
+ return;
+
+ numWaitingOnAddress++;
+ });
+
+ EXPECT_LE(numWaitingOnAddress, threads.size() - numSingleUnparks);
+
+ semaphore.store(threads.size() - numSingleUnparks);
+ ParkingLot::unparkAll(&semaphore);
+
+ numWaitingOnAddress = 0;
+ ParkingLot::forEach(
+ [&] (ThreadIdentifier, const void* address) {
+ if (address != &semaphore)
+ return;
+
+ numWaitingOnAddress++;
+ });
+
+ EXPECT_EQ(0u, numWaitingOnAddress);
+
+ {
+ std::unique_lock<std::mutex> locker(lock);
+ while (awake.size() < threads.size())
+ condition.wait(locker);
+ }
+
+ for (ThreadIdentifier threadIdentifier : threads)
+ waitForThreadCompletion(threadIdentifier);
+ }
+
+ // Semaphore operations.
+ void down()
+ {
+ for (;;) {
+ int oldSemaphoreValue = semaphore.load();
+ int newSemaphoreValue = oldSemaphoreValue - 1;
+ if (!semaphore.compareExchangeWeak(oldSemaphoreValue, newSemaphoreValue))
+ continue;
+
+ if (oldSemaphoreValue > 0) {
+ // We acquired the semaphore. Done.
+ return;
+ }
+
+ // We need to wait.
+ if (ParkingLot::compareAndPark(&semaphore, newSemaphoreValue)) {
+ // We did wait, and then got woken up. This means that someone who up'd the semaphore
+ // passed ownership onto us.
+ return;
+ }
+
+ // We never parked, because the semaphore value changed. Undo our decrement and try again.
+ for (;;) {
+ int oldSemaphoreValue = semaphore.load();
+ if (semaphore.compareExchangeWeak(oldSemaphoreValue, oldSemaphoreValue + 1))
+ break;
+ }
+ }
+ }
+ void up()
+ {
+ int oldSemaphoreValue;
+ for (;;) {
+ oldSemaphoreValue = semaphore.load();
+ if (semaphore.compareExchangeWeak(oldSemaphoreValue, oldSemaphoreValue + 1))
+ break;
+ }
+
+ // Check if anyone was waiting on the semaphore. If they were, then pass ownership to them.
+ if (oldSemaphoreValue < 0)
+ ParkingLot::unparkOne(&semaphore);
+ }
+
+ Atomic<int> semaphore;
+ std::mutex lock;
+ std::condition_variable condition;
+ HashSet<ThreadIdentifier> awake;
+ Vector<ThreadIdentifier> threads;
+ ThreadIdentifier lastAwoken { 0 };
+};
+
+void runParkingTest(unsigned numLatches, unsigned delay, unsigned numThreads, unsigned numSingleUnparks)
+{
+ std::unique_ptr<SingleLatchTest[]> tests = std::make_unique<SingleLatchTest[]>(numLatches);
+
+ for (unsigned latchIndex = numLatches; latchIndex--;)
+ tests[latchIndex].initialize(numThreads);
+
+ for (unsigned unparkIndex = 0; unparkIndex < numSingleUnparks; ++unparkIndex) {
+ std::this_thread::sleep_for(std::chrono::microseconds(delay));
+ for (unsigned latchIndex = numLatches; latchIndex--;)
+ tests[latchIndex].unparkOne(unparkIndex);
+ }
+
+ for (unsigned latchIndex = numLatches; latchIndex--;)
+ tests[latchIndex].finish(numSingleUnparks);
+}
+
+void repeatParkingTest(unsigned numRepeats, unsigned numLatches, unsigned delay, unsigned numThreads, unsigned numSingleUnparks)
+{
+ while (numRepeats--)
+ runParkingTest(numLatches, delay, numThreads, numSingleUnparks);
+}
+
+} // anonymous namespace
+
+TEST(WTF_ParkingLot, UnparkAllOneFast)
+{
+ repeatParkingTest(10000, 1, 0, 1, 0);
+}
+
+TEST(WTF_ParkingLot, UnparkAllHundredFast)
+{
+ repeatParkingTest(100, 1, 0, 100, 0);
+}
+
+TEST(WTF_ParkingLot, UnparkOneOneFast)
+{
+ repeatParkingTest(1000, 1, 0, 1, 1);
+}
+
+TEST(WTF_ParkingLot, UnparkOneHundredFast)
+{
+ repeatParkingTest(20, 1, 0, 100, 100);
+}
+
+TEST(WTF_ParkingLot, UnparkOneFiftyThenFiftyAllFast)
+{
+ repeatParkingTest(50, 1, 0, 100, 50);
+}
+
+TEST(WTF_ParkingLot, UnparkAllOne)
+{
+ repeatParkingTest(100, 1, 10000, 1, 0);
+}
+
+TEST(WTF_ParkingLot, UnparkAllHundred)
+{
+ repeatParkingTest(100, 1, 10000, 100, 0);
+}
+
+TEST(WTF_ParkingLot, UnparkOneOne)
+{
+ repeatParkingTest(10, 1, 10000, 1, 1);
+}
+
+TEST(WTF_ParkingLot, UnparkOneFifty)
+{
+ repeatParkingTest(1, 1, 10000, 50, 50);
+}
+
+TEST(WTF_ParkingLot, UnparkOneFiftyThenFiftyAll)
+{
+ repeatParkingTest(2, 1, 10000, 100, 50);
+}
+
+TEST(WTF_ParkingLot, HundredUnparkAllOneFast)
+{
+ repeatParkingTest(100, 100, 0, 1, 0);
+}
+
+TEST(WTF_ParkingLot, HundredUnparkAllOne)
+{
+ repeatParkingTest(1, 100, 10000, 1, 0);
+}
+
+} // namespace TestWebKitAPI
+
diff --git a/Tools/TestWebKitAPI/Tests/WTF/RedBlackTree.cpp b/Tools/TestWebKitAPI/Tests/WTF/RedBlackTree.cpp
new file mode 100644
index 000000000..5e5c8adc4
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/RedBlackTree.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <wtf/RedBlackTree.h>
+#include <wtf/Vector.h>
+
+using namespace WTF;
+
+namespace TestWebKitAPI {
+
+class TestNode : public RedBlackTree<TestNode, char>::Node {
+public:
+ TestNode(char key, unsigned value)
+ : m_key(key)
+ , m_value(value)
+ {
+ }
+
+ char key()
+ {
+ return m_key;
+ }
+
+ char m_key;
+ unsigned m_value;
+};
+
+class RedBlackTreeTest : public testing::Test {
+public:
+ unsigned m_counter;
+
+ virtual void SetUp()
+ {
+ m_counter = 0;
+ }
+
+ virtual void TearDown()
+ {
+ }
+
+ struct Pair {
+ char key;
+ unsigned value;
+
+ Pair() { }
+
+ Pair(char key, unsigned value)
+ : key(key)
+ , value(value)
+ {
+ }
+
+ bool operator==(const Pair& other) const
+ {
+ return key == other.key;
+ }
+
+ bool operator<(const Pair& other) const
+ {
+ return key < other.key;
+ }
+ };
+
+ typedef Vector<Pair, 16> PairVector;
+
+ PairVector findExact(PairVector& asVector, char key)
+ {
+ PairVector result;
+
+ for (size_t index = 0; index < asVector.size(); ++index) {
+ if (asVector.at(index).key == key)
+ result.append(asVector.at(index));
+ }
+
+ std::sort(result.begin(), result.end());
+
+ return result;
+ }
+
+ void remove(PairVector& asVector, size_t index)
+ {
+ asVector.at(index) = asVector.last();
+ asVector.removeLast();
+ }
+
+ PairVector findLeastGreaterThanOrEqual(PairVector& asVector, char key)
+ {
+ char bestKey = 0; // assignment to make gcc happy
+ bool foundKey = false;
+
+ for (size_t index = 0; index < asVector.size(); ++index) {
+ if (asVector.at(index).key >= key) {
+ if (asVector.at(index).key < bestKey || !foundKey) {
+ foundKey = true;
+ bestKey = asVector.at(index).key;
+ }
+ }
+ }
+
+ PairVector result;
+
+ if (!foundKey)
+ return result;
+
+ return findExact(asVector, bestKey);
+ }
+
+ void assertFoundAndRemove(PairVector& asVector, char key, unsigned value)
+ {
+ bool found = false;
+ size_t foundIndex = 0; // make compilers happy
+
+ for (size_t index = 0; index < asVector.size(); ++index) {
+ if (asVector.at(index).key == key
+ && asVector.at(index).value == value) {
+ EXPECT_TRUE(!found);
+
+ found = true;
+ foundIndex = index;
+ }
+ }
+
+ EXPECT_TRUE(found);
+
+ remove(asVector, foundIndex);
+ }
+
+ // This deliberately passes a copy of the vector.
+ void assertEqual(RedBlackTree<TestNode, char>& asTree, PairVector asVector)
+ {
+ for (TestNode* current = asTree.first(); current; current = current->successor())
+ assertFoundAndRemove(asVector, current->m_key, current->m_value);
+ }
+
+ void assertSameValuesForKey(RedBlackTree<TestNode, char>& asTree, TestNode* node, PairVector foundValues, char key)
+ {
+ if (node) {
+ EXPECT_EQ(node->m_key, key);
+
+ TestNode* prevNode = node;
+ do {
+ node = prevNode;
+ prevNode = prevNode->predecessor();
+ } while (prevNode && prevNode->m_key == key);
+
+ EXPECT_EQ(node->m_key, key);
+ EXPECT_TRUE(!prevNode || prevNode->m_key < key);
+
+ do {
+ assertFoundAndRemove(foundValues, node->m_key, node->m_value);
+
+ node = node->successor();
+ EXPECT_TRUE(!node || node->m_key >= key);
+ } while (node && node->m_key == key);
+ }
+
+ EXPECT_TRUE(foundValues.isEmpty());
+ }
+
+ // The control string is a null-terminated list of commands. Each
+ // command is two characters, with the first identifying the operation
+ // and the second giving a key. The commands are:
+ // +x Add x
+ // *x Find all elements equal to x
+ // @x Find all elements that have the smallest key that is greater than or equal to x
+ // !x Remove all elements equal to x
+ void testDriver(const char* controlString)
+ {
+ PairVector asVector;
+ RedBlackTree<TestNode, char> asTree;
+
+ for (const char* current = controlString; *current; current += 2) {
+ char command = current[0];
+ char key = current[1];
+ unsigned value = ++m_counter;
+
+ ASSERT(command);
+ ASSERT(key);
+
+ switch (command) {
+ case '+': {
+ TestNode* node = new TestNode(key, value);
+ asTree.insert(node);
+ asVector.append(Pair(key, value));
+ break;
+ }
+
+ case '*': {
+ TestNode* node = asTree.findExact(key);
+ if (node)
+ EXPECT_EQ(node->m_key, key);
+ assertSameValuesForKey(asTree, node, findExact(asVector, key), key);
+ break;
+ }
+
+ case '@': {
+ TestNode* node = asTree.findLeastGreaterThanOrEqual(key);
+ if (node) {
+ EXPECT_TRUE(node->m_key >= key);
+ assertSameValuesForKey(asTree, node, findLeastGreaterThanOrEqual(asVector, key), node->m_key);
+ } else
+ EXPECT_TRUE(findLeastGreaterThanOrEqual(asVector, key).isEmpty());
+ break;
+ }
+
+ case '!': {
+ while (true) {
+ TestNode* node = asTree.remove(key);
+ if (node) {
+ EXPECT_EQ(node->m_key, key);
+ assertFoundAndRemove(asVector, node->m_key, node->m_value);
+ } else {
+ EXPECT_TRUE(findExact(asVector, key).isEmpty());
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ EXPECT_EQ(asTree.size(), asVector.size());
+ assertEqual(asTree, asVector);
+ }
+ }
+};
+
+TEST_F(RedBlackTreeTest, Empty)
+{
+ testDriver("");
+}
+
+TEST_F(RedBlackTreeTest, EmptyGetFindRemove)
+{
+ testDriver("*x@y!z");
+}
+
+TEST_F(RedBlackTreeTest, SingleAdd)
+{
+ testDriver("+a");
+}
+
+TEST_F(RedBlackTreeTest, SingleAddGetFindRemoveNotFound)
+{
+ testDriver("+a*x@y!z");
+}
+
+TEST_F(RedBlackTreeTest, SingleAddGetFindRemove)
+{
+ testDriver("+a*a@a!a");
+}
+
+TEST_F(RedBlackTreeTest, TwoAdds)
+{
+ testDriver("+a+b");
+}
+
+TEST_F(RedBlackTreeTest, ThreeAdds)
+{
+ testDriver("+a+b+c");
+}
+
+TEST_F(RedBlackTreeTest, FourAdds)
+{
+ testDriver("+a+b+c+d");
+}
+
+TEST_F(RedBlackTreeTest, LotsOfRepeatAdds)
+{
+ testDriver("+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d");
+}
+
+TEST_F(RedBlackTreeTest, LotsOfRepeatAndUniqueAdds)
+{
+ testDriver("+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+u+v+x+y+z");
+}
+
+TEST_F(RedBlackTreeTest, LotsOfRepeatAndUniqueAddsAndGetsAndRemoves)
+{
+ testDriver("+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+u+v+x+y+z*a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z!a!b!c!d!e!f!g!h!i!j!k!l!m!n!o!p!q!r!s!t!u!v!w!x!y!z");
+}
+
+TEST_F(RedBlackTreeTest, SimpleBestFitSearch)
+{
+ testDriver("+d+d+m+w@d@m@w@a@g@q");
+}
+
+TEST_F(RedBlackTreeTest, BiggerBestFitSearch)
+{
+ testDriver("+d+d+d+d+d+d+d+d+d+d+f+f+f+f+f+f+f+h+h+i+j+k+l+m+o+p+q+r+z@a@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@s@t@u@v@w@x@y@z");
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/Ref.cpp b/Tools/TestWebKitAPI/Tests/WTF/Ref.cpp
new file mode 100644
index 000000000..8c622d07b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/Ref.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "RefLogger.h"
+#include <wtf/Ref.h>
+#include <wtf/RefPtr.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF_Ref, Basic)
+{
+ DerivedRefLogger a("a");
+
+ {
+ Ref<RefLogger> ptr(a);
+ ASSERT_EQ(&a, ptr.ptr());
+ ASSERT_EQ(&a.name, &ptr->name);
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ Ref<RefLogger> ptr(adoptRef(a));
+ ASSERT_EQ(&a, ptr.ptr());
+ ASSERT_EQ(&a.name, &ptr->name);
+ }
+ ASSERT_STREQ("deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_Ref, Assignment)
+{
+ DerivedRefLogger a("a");
+ RefLogger b("b");
+ DerivedRefLogger c("c");
+
+ {
+ Ref<RefLogger> ptr(a);
+ ASSERT_EQ(&a, ptr.ptr());
+ log() << "| ";
+ ptr = b;
+ ASSERT_EQ(&b, ptr.ptr());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) | ref(b) deref(a) | deref(b) ", takeLogStr().c_str());
+
+ {
+ Ref<RefLogger> ptr(a);
+ ASSERT_EQ(&a, ptr.ptr());
+ log() << "| ";
+ ptr = c;
+ ASSERT_EQ(&c, ptr.ptr());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) | ref(c) deref(a) | deref(c) ", takeLogStr().c_str());
+
+ {
+ Ref<RefLogger> ptr(a);
+ ASSERT_EQ(&a, ptr.ptr());
+ log() << "| ";
+ ptr = adoptRef(b);
+ ASSERT_EQ(&b, ptr.ptr());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) | deref(a) | deref(b) ", takeLogStr().c_str());
+
+ {
+ Ref<RefLogger> ptr(a);
+ ASSERT_EQ(&a, ptr.ptr());
+ log() << "| ";
+ ptr = adoptRef(c);
+ ASSERT_EQ(&c, ptr.ptr());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) | deref(a) | deref(c) ", takeLogStr().c_str());
+}
+
+static Ref<RefLogger> passWithRef(Ref<RefLogger>&& reference)
+{
+ return WTF::move(reference);
+}
+
+static RefPtr<RefLogger> passWithPassRefPtr(PassRefPtr<RefLogger> reference)
+{
+ return reference;
+}
+
+TEST(WTF_Ref, ReturnValue)
+{
+ DerivedRefLogger a("a");
+ RefLogger b("b");
+ DerivedRefLogger c("c");
+
+ {
+ Ref<RefLogger> ptr(passWithRef(Ref<RefLogger>(a)));
+ ASSERT_EQ(&a, ptr.ptr());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ Ref<RefLogger> ptr(a);
+ ASSERT_EQ(&a, ptr.ptr());
+ log() << "| ";
+ ptr = passWithRef(b);
+ ASSERT_EQ(&b, ptr.ptr());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) | ref(b) deref(a) | deref(b) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(passWithRef(a));
+ ASSERT_EQ(&a, ptr.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(passWithPassRefPtr(passWithRef(a)));
+ ASSERT_EQ(&a, ptr.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<DerivedRefLogger> ptr(&a);
+ RefPtr<RefLogger> ptr2(WTF::move(ptr));
+ ASSERT_EQ(nullptr, ptr.get());
+ ASSERT_EQ(&a, ptr2.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ Ref<DerivedRefLogger> derivedReference(a);
+ Ref<RefLogger> baseReference(passWithRef(derivedReference.copyRef()));
+ ASSERT_EQ(&a, derivedReference.ptr());
+ ASSERT_EQ(&a, baseReference.ptr());
+ }
+ ASSERT_STREQ("ref(a) ref(a) deref(a) deref(a) ", takeLogStr().c_str());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/RefCounter.cpp b/Tools/TestWebKitAPI/Tests/WTF/RefCounter.cpp
new file mode 100644
index 000000000..f8771e0c2
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/RefCounter.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/Ref.h>
+#include <wtf/RefCounter.h>
+#include <wtf/text/WTFString.h>
+
+namespace TestWebKitAPI {
+
+static const int CallbackExpected = 0xC0FFEE;
+static const int CallbackNotExpected = 0xDECAF;
+
+enum CounterType { };
+typedef RefCounter::Token<CounterType> TokenType;
+
+TEST(WTF, RefCounter)
+{
+ // RefCounter API is pretty simple, containing the following 4 methods to test:
+ //
+ // 1) RefCounter(std::function<void()>);
+ // 2) ~RefCounter();
+ // 3) Ref<Count> token() const;
+ // 4) unsigned value() const;
+ //
+ // We'll test:
+ // 1) Construction:
+ // 1a) with a callback
+ // 1b) without a callback
+ // 2) Destruction where the RefCounter::Count has:
+ // 2a) a non-zero reference count (Count outlives RefCounter).
+ // 2b) a zero reference count (Count is deleted by RefCounter's destructor).
+ // 3) Call count to ref/deref the Count object, where:
+ // 3a) ref with callback from 0 -> 1.
+ // 3b) ref with callback from 1 -> >1.
+ // 3c) deref with callback from >1 -> 1.
+ // 3d) deref with callback from 1 -> 0.
+ // 3d) deref with callback from 1 -> 0.
+ // 3e) ref with callback from 1 -> >1 AFTER RefCounter has been destroyed.
+ // 3f) deref with callback from >1 -> 1 AFTER RefCounter has been destroyed.
+ // 3g) deref with callback from 1 -> 0 AFTER RefCounter has been destroyed.
+ // 3h) ref without callback
+ // 3i) deref without callback
+ // 3j) ref using a Ref rather than a RefPtr (make sure there is no unnecessary reference count churn).
+ // 3k) deref using a Ref rather than a RefPtr (make sure there is no unnecessary reference count churn).
+ // 4) Test the value of the counter:
+ // 4a) at construction.
+ // 4b) as read within the callback.
+ // 4c) as read after the ref/deref.
+
+ // These values will outlive the following block.
+ int callbackValue = CallbackNotExpected;
+ TokenType incTo1Again;
+
+ {
+ // Testing (1a) - Construction with a callback.
+ RefCounter* counterPtr = nullptr;
+ RefCounter counter([&](bool value) {
+ // Check that the callback is called at the expected times, and the correct number of times.
+ EXPECT_EQ(callbackValue, CallbackExpected);
+ // Value provided should be equal to the counter value.
+ EXPECT_EQ(value, counterPtr->value());
+ // return the value of the counter in the callback.
+ callbackValue = value;
+ });
+ counterPtr = &counter;
+ // Testing (4a) - after construction value() is 0.
+ EXPECT_EQ(0, static_cast<int>(counter.value()));
+
+ // Testing (3a) - ref with callback from 0 -> 1.
+ callbackValue = CallbackExpected;
+ TokenType incTo1(counter.token<CounterType>());
+ // Testing (4b) & (4c) - values within & after callback.
+ EXPECT_EQ(true, callbackValue);
+ EXPECT_EQ(1, static_cast<int>(counter.value()));
+
+ // Testing (3b) - ref with callback from 1 -> 2.
+ TokenType incTo2(incTo1);
+ // Testing (4b) & (4c) - values within & after callback.
+ EXPECT_EQ(2, static_cast<int>(counter.value()));
+
+ // Testing (3c) - deref with callback from >1 -> 1.
+ incTo1 = nullptr;
+ // Testing (4b) & (4c) - values within & after callback.
+ EXPECT_EQ(1, static_cast<int>(counter.value()));
+
+ {
+ // Testing (3j) - ref using a Ref rather than a RefPtr.
+ TokenType incTo2Again(counter.token<CounterType>());
+ // Testing (4b) & (4c) - values within & after callback.
+ EXPECT_EQ(2, static_cast<int>(counter.value()));
+ // Testing (3k) - deref using a Ref rather than a RefPtr.
+ }
+ EXPECT_EQ(1, static_cast<int>(counter.value()));
+ // Testing (4b) & (4c) - values within & after callback.
+
+ // Testing (3d) - deref with callback from 1 -> 0.
+ callbackValue = CallbackExpected;
+ incTo2 = nullptr;
+ // Testing (4b) & (4c) - values within & after callback.
+ EXPECT_EQ(0, callbackValue);
+ EXPECT_EQ(0, static_cast<int>(counter.value()));
+
+ // Testing (2a) - Destruction where the RefCounter::Count has a non-zero reference count.
+ callbackValue = CallbackExpected;
+ incTo1Again = counter.token<CounterType>();
+ EXPECT_EQ(1, callbackValue);
+ EXPECT_EQ(1, static_cast<int>(counter.value()));
+ callbackValue = CallbackNotExpected;
+ }
+
+ // Testing (3e) - ref with callback from 1 -> >1 AFTER RefCounter has been destroyed.
+ TokenType incTo2Again = incTo1Again;
+ // Testing (3f) - deref with callback from >1 -> 1 AFTER RefCounter has been destroyed.
+ incTo1Again = nullptr;
+ // Testing (3g) - deref with callback from 1 -> 0 AFTER RefCounter has been destroyed.
+ incTo2Again = nullptr;
+
+ // Testing (1b) - Construction without a callback.
+ RefCounter counter;
+ // Testing (4a) - after construction value() is 0.
+ EXPECT_EQ(0, static_cast<int>(counter.value()));
+ // Testing (3h) - ref without callback
+ TokenType incTo1(counter.token<CounterType>());
+ // Testing (4c) - value as read after the ref.
+ EXPECT_EQ(1, static_cast<int>(counter.value()));
+ // Testing (3i) - deref without callback
+ incTo1 = nullptr;
+ // Testing (4c) - value as read after the deref.
+ EXPECT_EQ(0, static_cast<int>(counter.value()));
+ // Testing (2b) - Destruction where the RefCounter::Count has a zero reference count.
+ // ... not a lot to test here! - we can at least ensure this code path is run & we don't crash!
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/RefLogger.h b/Tools/TestWebKitAPI/Tests/WTF/RefLogger.h
new file mode 100644
index 000000000..c5cfb071f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/RefLogger.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RefLogger_h
+
+namespace TestWebKitAPI {
+
+inline std::ostringstream& log()
+{
+ static std::ostringstream log;
+ return log;
+}
+
+inline std::string takeLogStr()
+{
+ std::string string = log().str();
+ log().str("");
+ return string;
+}
+
+struct RefLogger {
+ RefLogger(const char* name) : name(*name) { }
+ void ref() { log() << "ref(" << &name << ") "; }
+ void deref() { log() << "deref(" << &name << ") "; }
+ const char& name;
+};
+
+struct DerivedRefLogger : RefLogger {
+ DerivedRefLogger(const char* name) : RefLogger(name) { log().str(""); }
+};
+
+}
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp
new file mode 100644
index 000000000..b726d7ddf
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "RefLogger.h"
+#include <wtf/RefPtr.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF_RefPtr, Basic)
+{
+ DerivedRefLogger a("a");
+
+ RefPtr<RefLogger> empty;
+ ASSERT_EQ(nullptr, empty.get());
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ASSERT_EQ(&a, &*ptr);
+ ASSERT_EQ(&a.name, &ptr->name);
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr = &a;
+ ASSERT_EQ(&a, ptr.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1 = &a;
+ RefPtr<RefLogger> p2(p1);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+ ASSERT_STREQ("ref(a) ref(a) deref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1 = &a;
+ RefPtr<RefLogger> p2 = p1;
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+ ASSERT_STREQ("ref(a) ref(a) deref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1 = &a;
+ RefPtr<RefLogger> p2 = WTF::move(p1);
+ ASSERT_EQ(nullptr, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1 = &a;
+ RefPtr<RefLogger> p2(WTF::move(p1));
+ ASSERT_EQ(nullptr, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<DerivedRefLogger> p1 = &a;
+ RefPtr<RefLogger> p2 = p1;
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+ ASSERT_STREQ("ref(a) ref(a) deref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<DerivedRefLogger> p1 = &a;
+ RefPtr<RefLogger> p2 = WTF::move(p1);
+ ASSERT_EQ(nullptr, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ptr = nullptr;
+ ASSERT_EQ(nullptr, ptr.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ptr.release();
+ ASSERT_EQ(nullptr, ptr.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_RefPtr, AssignPassRefToRefPtr)
+{
+ DerivedRefLogger a("a");
+ {
+ Ref<RefLogger> passRef(a);
+ RefPtr<RefLogger> ptr = WTF::move(passRef);
+ ASSERT_EQ(&a, ptr.get());
+ ptr.release();
+ ASSERT_EQ(nullptr, ptr.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_RefPtr, Adopt)
+{
+ DerivedRefLogger a("a");
+
+ RefPtr<RefLogger> empty;
+ ASSERT_EQ(nullptr, empty.get());
+
+ {
+ RefPtr<RefLogger> ptr(adoptRef(&a));
+ ASSERT_EQ(&a, ptr.get());
+ ASSERT_EQ(&a, &*ptr);
+ ASSERT_EQ(&a.name, &ptr->name);
+ }
+ ASSERT_STREQ("deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr = adoptRef(&a);
+ ASSERT_EQ(&a, ptr.get());
+ }
+ ASSERT_STREQ("deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_RefPtr, Assignment)
+{
+ DerivedRefLogger a("a");
+ RefLogger b("b");
+ DerivedRefLogger c("c");
+
+ {
+ RefPtr<RefLogger> p1(&a);
+ RefPtr<RefLogger> p2(&b);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ log() << "| ";
+ p1 = p2;
+ ASSERT_EQ(&b, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) ref(b) | ref(b) deref(a) | deref(b) deref(b) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ log() << "| ";
+ ptr = &b;
+ ASSERT_EQ(&b, ptr.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) | ref(b) deref(a) | deref(b) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ log() << "| ";
+ ptr = adoptRef(&b);
+ ASSERT_EQ(&b, ptr.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) | deref(a) | deref(b) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ptr = nullptr;
+ ASSERT_EQ(nullptr, ptr.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1(&a);
+ RefPtr<RefLogger> p2(&b);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ log() << "| ";
+ p1 = WTF::move(p2);
+ ASSERT_EQ(&b, p1.get());
+ ASSERT_EQ(nullptr, p2.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) ref(b) | deref(a) | deref(b) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1(&a);
+ RefPtr<DerivedRefLogger> p2(&c);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&c, p2.get());
+ log() << "| ";
+ p1 = p2;
+ ASSERT_EQ(&c, p1.get());
+ ASSERT_EQ(&c, p2.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) ref(c) | ref(c) deref(a) | deref(c) deref(c) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ log() << "| ";
+ ptr = &c;
+ ASSERT_EQ(&c, ptr.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) | ref(c) deref(a) | deref(c) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ log() << "| ";
+ ptr = adoptRef(&c);
+ ASSERT_EQ(&c, ptr.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) | deref(a) | deref(c) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1(&a);
+ RefPtr<DerivedRefLogger> p2(&c);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&c, p2.get());
+ log() << "| ";
+ p1 = WTF::move(p2);
+ ASSERT_EQ(&c, p1.get());
+ ASSERT_EQ(nullptr, p2.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) ref(c) | deref(a) | deref(c) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ log() << "| ";
+ ptr = ptr;
+ ASSERT_EQ(&a, ptr.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) | ref(a) deref(a) | deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> ptr(&a);
+ ASSERT_EQ(&a, ptr.get());
+ ptr = WTF::move(ptr);
+ ASSERT_EQ(&a, ptr.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_RefPtr, Swap)
+{
+ RefLogger a("a");
+ RefLogger b("b");
+
+ {
+ RefPtr<RefLogger> p1(&a);
+ RefPtr<RefLogger> p2(&b);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ p1.swap(p2);
+ ASSERT_EQ(&b, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) ref(b) | deref(a) deref(b) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1(&a);
+ RefPtr<RefLogger> p2(&b);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ std::swap(p1, p2);
+ ASSERT_EQ(&b, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) ref(b) | deref(a) deref(b) ", takeLogStr().c_str());
+}
+
+TEST(WTF_RefPtr, ReleaseNonNull)
+{
+ RefLogger a("a");
+
+ {
+ RefPtr<RefLogger> refPtr = &a;
+ RefPtr<RefLogger> ref = refPtr.releaseNonNull();
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_RefPtr, Release)
+{
+ DerivedRefLogger a("a");
+ RefLogger b("b");
+ DerivedRefLogger c("c");
+
+ {
+ RefPtr<RefLogger> p1 = &a;
+ RefPtr<RefLogger> p2 = p1.release();
+ ASSERT_EQ(nullptr, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1 = &a;
+ RefPtr<RefLogger> p2(p1.release());
+ ASSERT_EQ(nullptr, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<DerivedRefLogger> p1 = &a;
+ RefPtr<RefLogger> p2 = p1.release();
+ ASSERT_EQ(nullptr, p1.get());
+ ASSERT_EQ(&a, p2.get());
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1(&a);
+ RefPtr<RefLogger> p2(&b);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&b, p2.get());
+ log() << "| ";
+ p1 = p2.release();
+ ASSERT_EQ(&b, p1.get());
+ ASSERT_EQ(nullptr, p2.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) ref(b) | deref(a) | deref(b) ", takeLogStr().c_str());
+
+ {
+ RefPtr<RefLogger> p1(&a);
+ RefPtr<DerivedRefLogger> p2(&c);
+ ASSERT_EQ(&a, p1.get());
+ ASSERT_EQ(&c, p2.get());
+ log() << "| ";
+ p1 = p2.release();
+ ASSERT_EQ(&c, p1.get());
+ ASSERT_EQ(nullptr, p2.get());
+ log() << "| ";
+ }
+ ASSERT_STREQ("ref(a) ref(c) | deref(a) | deref(c) ", takeLogStr().c_str());
+}
+
+RefPtr<RefLogger> f1(RefLogger& logger)
+{
+ return RefPtr<RefLogger>(&logger);
+}
+
+TEST(WTF_RefPtr, ReturnValue)
+{
+ DerivedRefLogger a("a");
+
+ {
+ f1(a);
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ auto ptr = f1(a);
+ }
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/RunLoop.cpp b/Tools/TestWebKitAPI/Tests/WTF/RunLoop.cpp
new file mode 100644
index 000000000..e7e3d9f99
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/RunLoop.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "PlatformUtilities.h"
+#include <wtf/RunLoop.h>
+
+namespace TestWebKitAPI {
+
+static bool testFinished;
+static int count = 100;
+
+TEST(WTF_RunLoop, Deadlock)
+{
+ RunLoop::initializeMainRunLoop();
+
+ struct DispatchFromDestructorTester {
+ ~DispatchFromDestructorTester() {
+ RunLoop::main().dispatch([] {
+ if (!(--count))
+ testFinished = true;
+ });
+ }
+ };
+
+ for (int i = 0; i < count; ++i) {
+ auto capture = std::make_shared<DispatchFromDestructorTester>();
+ RunLoop::main().dispatch([capture] { });
+ }
+
+ Util::run(&testFinished);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/SHA1.cpp b/Tools/TestWebKitAPI/Tests/WTF/SHA1.cpp
new file mode 100644
index 000000000..dcf9d27b3
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/SHA1.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <wtf/SHA1.h>
+#include <wtf/text/CString.h>
+
+namespace TestWebKitAPI {
+
+static void expectSHA1(CString input, int repeat, CString expected)
+{
+ SHA1 sha1;
+ for (int i = 0; i < repeat; ++i)
+ sha1.addBytes(input);
+ CString actual = sha1.computeHexDigest();
+
+ ASSERT_EQ(expected.length(), actual.length());
+ ASSERT_STREQ(expected.data(), actual.data());
+}
+
+TEST(WTF_SHA1, Computation)
+{
+ // Examples taken from sample code in RFC 3174.
+ expectSHA1("abc", 1, "A9993E364706816ABA3E25717850C26C9CD0D89D");
+ expectSHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 1, "84983E441C3BD26EBAAE4AA1F95129E5E54670F1");
+ expectSHA1("a", 1000000, "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F");
+ expectSHA1("0123456701234567012345670123456701234567012345670123456701234567", 10, "DEA356A2CDDD90C7A7ECEDC5EBB563934F460452");
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/SaturatedArithmeticOperations.cpp b/Tools/TestWebKitAPI/Tests/WTF/SaturatedArithmeticOperations.cpp
new file mode 100644
index 000000000..e854e256e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/SaturatedArithmeticOperations.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2012, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "limits.h"
+#include <wtf/SaturatedArithmetic.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF, SaturatedArithmeticAddition)
+{
+ ASSERT_EQ(saturatedAddition(0, 0), 0);
+ ASSERT_EQ(saturatedAddition(0, 1), 1);
+ ASSERT_EQ(saturatedAddition(0, 100), 100);
+ ASSERT_EQ(saturatedAddition(100, 50), 150);
+
+ ASSERT_EQ(saturatedAddition(0, -1), -1);
+ ASSERT_EQ(saturatedAddition(1, -1), 0);
+ ASSERT_EQ(saturatedAddition(100, -50), 50);
+ ASSERT_EQ(saturatedAddition(50, -100), -50);
+
+ ASSERT_EQ(saturatedAddition(INT_MAX - 1, 0), INT_MAX - 1);
+ ASSERT_EQ(saturatedAddition(INT_MAX - 1, 1), INT_MAX);
+ ASSERT_EQ(saturatedAddition(INT_MAX - 1, 2), INT_MAX);
+ ASSERT_EQ(saturatedAddition(0, INT_MAX - 1), INT_MAX - 1);
+ ASSERT_EQ(saturatedAddition(1, INT_MAX - 1), INT_MAX);
+ ASSERT_EQ(saturatedAddition(2, INT_MAX - 1), INT_MAX);
+ ASSERT_EQ(saturatedAddition(INT_MAX - 1, INT_MAX - 1), INT_MAX);
+ ASSERT_EQ(saturatedAddition(INT_MAX, INT_MAX), INT_MAX);
+
+ ASSERT_EQ(saturatedAddition(INT_MIN, 0), INT_MIN);
+ ASSERT_EQ(saturatedAddition(INT_MIN + 1, 0), INT_MIN + 1);
+ ASSERT_EQ(saturatedAddition(INT_MIN + 1, 1), INT_MIN + 2);
+ ASSERT_EQ(saturatedAddition(INT_MIN + 1, 2), INT_MIN + 3);
+ ASSERT_EQ(saturatedAddition(INT_MIN + 1, -1), INT_MIN);
+ ASSERT_EQ(saturatedAddition(INT_MIN + 1, -2), INT_MIN);
+ ASSERT_EQ(saturatedAddition(0, INT_MIN + 1), INT_MIN + 1);
+ ASSERT_EQ(saturatedAddition(-1, INT_MIN + 1), INT_MIN);
+ ASSERT_EQ(saturatedAddition(-2, INT_MIN + 1), INT_MIN);
+
+ ASSERT_EQ(saturatedAddition(INT_MAX / 2, 10000), INT_MAX / 2 + 10000);
+ ASSERT_EQ(saturatedAddition(INT_MAX / 2 + 1, INT_MAX / 2 + 1), INT_MAX);
+ ASSERT_EQ(saturatedAddition(INT_MIN, INT_MAX), -1);
+}
+
+TEST(WTF, SaturatedArithmeticSubtraction)
+{
+ ASSERT_EQ(saturatedSubtraction(0, 0), 0);
+ ASSERT_EQ(saturatedSubtraction(0, 1), -1);
+ ASSERT_EQ(saturatedSubtraction(0, 100), -100);
+ ASSERT_EQ(saturatedSubtraction(100, 50), 50);
+
+ ASSERT_EQ(saturatedSubtraction(0, -1), 1);
+ ASSERT_EQ(saturatedSubtraction(1, -1), 2);
+ ASSERT_EQ(saturatedSubtraction(100, -50), 150);
+ ASSERT_EQ(saturatedSubtraction(50, -100), 150);
+
+ ASSERT_EQ(saturatedSubtraction(INT_MAX, 0), INT_MAX);
+ ASSERT_EQ(saturatedSubtraction(INT_MAX, 1), INT_MAX - 1);
+ ASSERT_EQ(saturatedSubtraction(INT_MAX - 1, 0), INT_MAX - 1);
+ ASSERT_EQ(saturatedSubtraction(INT_MAX - 1, -1), INT_MAX);
+ ASSERT_EQ(saturatedSubtraction(INT_MAX - 1, -2), INT_MAX);
+ ASSERT_EQ(saturatedSubtraction(0, INT_MAX - 1), -INT_MAX + 1);
+ ASSERT_EQ(saturatedSubtraction(-1, INT_MAX - 1), -INT_MAX);
+ ASSERT_EQ(saturatedSubtraction(-2, INT_MAX - 1), -INT_MAX - 1);
+ ASSERT_EQ(saturatedSubtraction(-3, INT_MAX - 1), -INT_MAX - 1);
+
+ ASSERT_EQ(saturatedSubtraction(INT_MIN, 0), INT_MIN);
+ ASSERT_EQ(saturatedSubtraction(INT_MIN + 1, 0), INT_MIN + 1);
+ ASSERT_EQ(saturatedSubtraction(INT_MIN + 1, 1), INT_MIN);
+ ASSERT_EQ(saturatedSubtraction(INT_MIN + 1, 2), INT_MIN);
+
+ ASSERT_EQ(saturatedSubtraction(INT_MIN, INT_MIN), 0);
+ ASSERT_EQ(saturatedSubtraction(INT_MAX, INT_MAX), 0);
+ ASSERT_EQ(saturatedSubtraction(INT_MAX, INT_MIN), INT_MAX);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringBuilder.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringBuilder.cpp
new file mode 100644
index 000000000..11bf3590e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/StringBuilder.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WTFStringUtilities.h"
+
+namespace TestWebKitAPI {
+
+static void expectBuilderContent(const String& expected, const StringBuilder& builder)
+{
+ // Not using builder.toString() or builder.toStringPreserveCapacity() because they all
+ // change internal state of builder.
+ if (builder.is8Bit())
+ EXPECT_EQ(expected, String(builder.characters8(), builder.length()));
+ else
+ EXPECT_EQ(expected, String(builder.characters16(), builder.length()));
+}
+
+void expectEmpty(const StringBuilder& builder)
+{
+ EXPECT_EQ(0U, builder.length());
+ EXPECT_TRUE(builder.isEmpty());
+ EXPECT_EQ(0, builder.characters8());
+}
+
+TEST(StringBuilderTest, DefaultConstructor)
+{
+ StringBuilder builder;
+ expectEmpty(builder);
+}
+
+TEST(StringBuilderTest, Append)
+{
+ StringBuilder builder;
+ builder.append(String("0123456789"));
+ expectBuilderContent("0123456789", builder);
+ builder.append("abcd");
+ expectBuilderContent("0123456789abcd", builder);
+ builder.append("efgh", 3);
+ expectBuilderContent("0123456789abcdefg", builder);
+ builder.append("");
+ expectBuilderContent("0123456789abcdefg", builder);
+ builder.append('#');
+ expectBuilderContent("0123456789abcdefg#", builder);
+
+ builder.toString(); // Test after reifyString().
+ StringBuilder builder1;
+ builder.append("", 0);
+ expectBuilderContent("0123456789abcdefg#", builder);
+ builder1.append(builder.characters8(), builder.length());
+ builder1.append("XYZ");
+ builder.append(builder1.characters8(), builder1.length());
+ expectBuilderContent("0123456789abcdefg#0123456789abcdefg#XYZ", builder);
+
+ StringBuilder builder2;
+ builder2.reserveCapacity(100);
+ builder2.append("xyz");
+ const LChar* characters = builder2.characters8();
+ builder2.append("0123456789");
+ ASSERT_EQ(characters, builder2.characters8());
+ builder2.toStringPreserveCapacity(); // Test after reifyString with buffer preserved.
+ builder2.append("abcd");
+ ASSERT_EQ(characters, builder2.characters8());
+
+ // Test appending UChar32 characters to StringBuilder.
+ StringBuilder builderForUChar32Append;
+ UChar32 frakturAChar = 0x1D504;
+ builderForUChar32Append.append(frakturAChar); // The fraktur A is not in the BMP, so it's two UTF-16 code units long.
+ ASSERT_EQ(2U, builderForUChar32Append.length());
+ builderForUChar32Append.append(static_cast<UChar32>('A'));
+ ASSERT_EQ(3U, builderForUChar32Append.length());
+ const UChar resultArray[] = { U16_LEAD(frakturAChar), U16_TRAIL(frakturAChar), 'A' };
+ expectBuilderContent(String(resultArray, WTF_ARRAY_LENGTH(resultArray)), builderForUChar32Append);
+}
+
+TEST(StringBuilderTest, ToString)
+{
+ StringBuilder builder;
+ builder.append("0123456789");
+ String string = builder.toString();
+ ASSERT_EQ(String("0123456789"), string);
+ ASSERT_EQ(string.impl(), builder.toString().impl());
+
+ // Changing the StringBuilder should not affect the original result of toString().
+ builder.append("abcdefghijklmnopqrstuvwxyz");
+ ASSERT_EQ(String("0123456789"), string);
+
+ // Changing the StringBuilder should not affect the original result of toString() in case the capacity is not changed.
+ builder.reserveCapacity(200);
+ string = builder.toString();
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
+ builder.append("ABC");
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
+
+ // Changing the original result of toString() should not affect the content of the StringBuilder.
+ String string1 = builder.toString();
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
+ string1.append("DEF");
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), builder.toString());
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABCDEF"), string1);
+
+ // Resizing the StringBuilder should not affect the original result of toString().
+ string1 = builder.toString();
+ builder.resize(10);
+ builder.append("###");
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
+}
+
+TEST(StringBuilderTest, ToStringPreserveCapacity)
+{
+ StringBuilder builder;
+ builder.append("0123456789");
+ unsigned capacity = builder.capacity();
+ String string = builder.toStringPreserveCapacity();
+ ASSERT_EQ(capacity, builder.capacity());
+ ASSERT_EQ(String("0123456789"), string);
+ ASSERT_EQ(string.impl(), builder.toStringPreserveCapacity().impl());
+ ASSERT_EQ(string.characters8(), builder.characters8());
+
+ // Changing the StringBuilder should not affect the original result of toStringPreserveCapacity().
+ builder.append("abcdefghijklmnopqrstuvwxyz");
+ ASSERT_EQ(String("0123456789"), string);
+
+ // Changing the StringBuilder should not affect the original result of toStringPreserveCapacity() in case the capacity is not changed.
+ builder.reserveCapacity(200);
+ capacity = builder.capacity();
+ string = builder.toStringPreserveCapacity();
+ ASSERT_EQ(capacity, builder.capacity());
+ ASSERT_EQ(string.characters8(), builder.characters8());
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
+ builder.append("ABC");
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
+
+ // Changing the original result of toStringPreserveCapacity() should not affect the content of the StringBuilder.
+ capacity = builder.capacity();
+ String string1 = builder.toStringPreserveCapacity();
+ ASSERT_EQ(capacity, builder.capacity());
+ ASSERT_EQ(string1.characters8(), builder.characters8());
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
+ string1.append("DEF");
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), builder.toStringPreserveCapacity());
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABCDEF"), string1);
+
+ // Resizing the StringBuilder should not affect the original result of toStringPreserveCapacity().
+ capacity = builder.capacity();
+ string1 = builder.toStringPreserveCapacity();
+ ASSERT_EQ(capacity, builder.capacity());
+ ASSERT_EQ(string.characters8(), builder.characters8());
+ builder.resize(10);
+ builder.append("###");
+ ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
+}
+
+TEST(StringBuilderTest, Clear)
+{
+ StringBuilder builder;
+ builder.append("0123456789");
+ builder.clear();
+ expectEmpty(builder);
+}
+
+TEST(StringBuilderTest, Array)
+{
+ StringBuilder builder;
+ builder.append("0123456789");
+ EXPECT_EQ('0', static_cast<char>(builder[0]));
+ EXPECT_EQ('9', static_cast<char>(builder[9]));
+ builder.toString(); // Test after reifyString().
+ EXPECT_EQ('0', static_cast<char>(builder[0]));
+ EXPECT_EQ('9', static_cast<char>(builder[9]));
+}
+
+TEST(StringBuilderTest, Resize)
+{
+ StringBuilder builder;
+ builder.append("0123456789");
+ builder.resize(10);
+ EXPECT_EQ(10U, builder.length());
+ expectBuilderContent("0123456789", builder);
+ builder.resize(8);
+ EXPECT_EQ(8U, builder.length());
+ expectBuilderContent("01234567", builder);
+
+ builder.toString();
+ builder.resize(7);
+ EXPECT_EQ(7U, builder.length());
+ expectBuilderContent("0123456", builder);
+ builder.resize(0);
+ expectEmpty(builder);
+}
+
+TEST(StringBuilderTest, Equal)
+{
+ StringBuilder builder1;
+ StringBuilder builder2;
+ ASSERT_TRUE(builder1 == builder2);
+ ASSERT_TRUE(equal(builder1, static_cast<LChar*>(0), 0));
+ ASSERT_TRUE(builder1 == String());
+ ASSERT_TRUE(String() == builder1);
+ ASSERT_TRUE(builder1 != String("abc"));
+
+ builder1.append("123");
+ builder1.reserveCapacity(32);
+ builder2.append("123");
+ builder1.reserveCapacity(64);
+ ASSERT_TRUE(builder1 == builder2);
+ ASSERT_TRUE(builder1 == String("123"));
+ ASSERT_TRUE(String("123") == builder1);
+
+ builder2.append("456");
+ ASSERT_TRUE(builder1 != builder2);
+ ASSERT_TRUE(builder2 != builder1);
+ ASSERT_TRUE(String("123") != builder2);
+ ASSERT_TRUE(builder2 != String("123"));
+ builder2.toString(); // Test after reifyString().
+ ASSERT_TRUE(builder1 != builder2);
+
+ builder2.resize(3);
+ ASSERT_TRUE(builder1 == builder2);
+
+ builder1.toString(); // Test after reifyString().
+ ASSERT_TRUE(builder1 == builder2);
+}
+
+TEST(StringBuilderTest, CanShrink)
+{
+ StringBuilder builder;
+ builder.reserveCapacity(256);
+ ASSERT_TRUE(builder.canShrink());
+ for (int i = 0; i < 256; i++)
+ builder.append('x');
+ ASSERT_EQ(builder.length(), builder.capacity());
+ ASSERT_FALSE(builder.canShrink());
+}
+
+TEST(StringBuilderTest, ToAtomicString)
+{
+ StringBuilder builder;
+ builder.append("123");
+ AtomicString atomicString = builder.toAtomicString();
+ ASSERT_EQ(String("123"), atomicString);
+
+ builder.reserveCapacity(256);
+ ASSERT_TRUE(builder.canShrink());
+ for (int i = builder.length(); i < 128; i++)
+ builder.append('x');
+ AtomicString atomicString1 = builder.toAtomicString();
+ ASSERT_EQ(128u, atomicString1.length());
+ ASSERT_EQ('x', atomicString1[127]);
+
+ // Later change of builder should not affect the atomic string.
+ for (int i = builder.length(); i < 256; i++)
+ builder.append('x');
+ ASSERT_EQ(128u, atomicString1.length());
+
+ ASSERT_FALSE(builder.canShrink());
+ String string = builder.toString();
+ AtomicString atomicString2 = builder.toAtomicString();
+ // They should share the same StringImpl.
+ ASSERT_EQ(atomicString2.impl(), string.impl());
+}
+
+TEST(StringBuilderTest, ToAtomicStringOnEmpty)
+{
+ { // Default constructed.
+ StringBuilder builder;
+ AtomicString atomicString = builder.toAtomicString();
+ ASSERT_EQ(emptyAtom, atomicString);
+ }
+ { // With capacity.
+ StringBuilder builder;
+ builder.reserveCapacity(64);
+ AtomicString atomicString = builder.toAtomicString();
+ ASSERT_EQ(emptyAtom, atomicString);
+ }
+ { // AtomicString constructed from a null string.
+ StringBuilder builder;
+ builder.append(String());
+ AtomicString atomicString = builder.toAtomicString();
+ ASSERT_EQ(emptyAtom, atomicString);
+ }
+ { // AtomicString constructed from an empty string.
+ StringBuilder builder;
+ builder.append(emptyString());
+ AtomicString atomicString = builder.toAtomicString();
+ ASSERT_EQ(emptyAtom, atomicString);
+ }
+ { // AtomicString constructed from an empty StringBuilder.
+ StringBuilder builder;
+ StringBuilder emptyBuilder;
+ builder.append(emptyBuilder);
+ AtomicString atomicString = builder.toAtomicString();
+ ASSERT_EQ(emptyAtom, atomicString);
+ }
+ { // AtomicString constructed from an empty char* string.
+ StringBuilder builder;
+ builder.append("", 0);
+ AtomicString atomicString = builder.toAtomicString();
+ ASSERT_EQ(emptyAtom, atomicString);
+ }
+ { // Cleared StringBuilder.
+ StringBuilder builder;
+ builder.appendLiteral("WebKit");
+ builder.clear();
+ AtomicString atomicString = builder.toAtomicString();
+ ASSERT_EQ(emptyAtom, atomicString);
+ }
+}
+
+} // namespace
diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp
new file mode 100644
index 000000000..a4d223c99
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/StringHasher.h>
+
+namespace TestWebKitAPI {
+
+static const LChar nullLChars[2] = { 0, 0 };
+static const UChar nullUChars[2] = { 0, 0 };
+
+static const unsigned emptyStringHash = 0x4EC889EU;
+static const unsigned singleNullCharacterHash = 0x3D3ABF44U;
+
+static const LChar testALChars[6] = { 0x41, 0x95, 0xFF, 0x50, 0x01, 0 };
+static const UChar testAUChars[6] = { 0x41, 0x95, 0xFF, 0x50, 0x01, 0 };
+static const UChar testBUChars[6] = { 0x41, 0x95, 0xFFFF, 0x1080, 0x01, 0 };
+
+static const unsigned testAHash1 = 0xEA32B004;
+static const unsigned testAHash2 = 0x93F0F71E;
+static const unsigned testAHash3 = 0xCB609EB1;
+static const unsigned testAHash4 = 0x7984A706;
+static const unsigned testAHash5 = 0x0427561F;
+
+static const unsigned testBHash1 = 0xEA32B004;
+static const unsigned testBHash2 = 0x93F0F71E;
+static const unsigned testBHash3 = 0x59EB1B2C;
+static const unsigned testBHash4 = 0xA7BCCC0A;
+static const unsigned testBHash5 = 0x79201649;
+
+TEST(WTF, StringHasher)
+{
+ StringHasher hasher;
+
+ // The initial state of the hasher.
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+}
+
+TEST(WTF, StringHasher_addCharacter)
+{
+ StringHasher hasher;
+
+ // Hashing a single character.
+ hasher = StringHasher();
+ hasher.addCharacter(0);
+ ASSERT_EQ(singleNullCharacterHash, hasher.hash());
+ ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+
+ // Hashing five characters, checking the intermediate state after each is added.
+ hasher = StringHasher();
+ hasher.addCharacter(testAUChars[0]);
+ ASSERT_EQ(testAHash1, hasher.hash());
+ ASSERT_EQ(testAHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacter(testAUChars[1]);
+ ASSERT_EQ(testAHash2, hasher.hash());
+ ASSERT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacter(testAUChars[2]);
+ ASSERT_EQ(testAHash3, hasher.hash());
+ ASSERT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacter(testAUChars[3]);
+ ASSERT_EQ(testAHash4, hasher.hash());
+ ASSERT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacter(testAUChars[4]);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+
+ // Hashing a second set of five characters, including non-Latin-1 characters.
+ hasher = StringHasher();
+ hasher.addCharacter(testBUChars[0]);
+ ASSERT_EQ(testBHash1, hasher.hash());
+ ASSERT_EQ(testBHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacter(testBUChars[1]);
+ ASSERT_EQ(testBHash2, hasher.hash());
+ ASSERT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacter(testBUChars[2]);
+ ASSERT_EQ(testBHash3, hasher.hash());
+ ASSERT_EQ(testBHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacter(testBUChars[3]);
+ ASSERT_EQ(testBHash4, hasher.hash());
+ ASSERT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacter(testBUChars[4]);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+}
+
+TEST(WTF, StringHasher_addCharacters)
+{
+ StringHasher hasher;
+
+ // Hashing zero characters.
+ hasher = StringHasher();
+ hasher.addCharacters(static_cast<LChar*>(0), 0);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(nullLChars, 0);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(nullLChars);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(static_cast<UChar*>(0), 0);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(nullUChars, 0);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(nullUChars);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+
+ // Hashing one character.
+ hasher = StringHasher();
+ hasher.addCharacters(nullLChars, 1);
+ ASSERT_EQ(singleNullCharacterHash, hasher.hash());
+ ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(nullUChars, 1);
+ ASSERT_EQ(singleNullCharacterHash, hasher.hash());
+ ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+
+ // Hashing five characters, all at once.
+ hasher = StringHasher();
+ hasher.addCharacters(testALChars, 5);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testALChars);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testAUChars, 5);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testAUChars);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testBUChars, 5);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testBUChars);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+
+ // Hashing five characters, in groups of two, then the last one.
+ hasher = StringHasher();
+ hasher.addCharacters(testALChars, 2);
+ ASSERT_EQ(testAHash2, hasher.hash());
+ ASSERT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testALChars + 2, 2);
+ ASSERT_EQ(testAHash4, hasher.hash());
+ ASSERT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testALChars + 4, 1);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testALChars, 2);
+ hasher.addCharacters(testALChars + 2, 2);
+ hasher.addCharacters(testALChars + 4);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testAUChars, 2);
+ ASSERT_EQ(testAHash2, hasher.hash());
+ ASSERT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testAUChars + 2, 2);
+ ASSERT_EQ(testAHash4, hasher.hash());
+ ASSERT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testAUChars + 4, 1);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testAUChars, 2);
+ hasher.addCharacters(testAUChars + 2, 2);
+ hasher.addCharacters(testAUChars + 4);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testBUChars, 2);
+ ASSERT_EQ(testBHash2, hasher.hash());
+ ASSERT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testBUChars + 2, 2);
+ ASSERT_EQ(testBHash4, hasher.hash());
+ ASSERT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testBUChars + 4, 1);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testBUChars, 2);
+ hasher.addCharacters(testBUChars + 2, 2);
+ hasher.addCharacters(testBUChars + 4);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+
+ // Hashing five characters, the first three, then the last two.
+ hasher = StringHasher();
+ hasher.addCharacters(testALChars, 3);
+ ASSERT_EQ(testAHash3, hasher.hash());
+ ASSERT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testALChars + 3, 2);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testALChars, 3);
+ ASSERT_EQ(testAHash3, hasher.hash());
+ ASSERT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testALChars + 3);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testAUChars, 3);
+ ASSERT_EQ(testAHash3, hasher.hash());
+ ASSERT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testAUChars + 3, 2);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testAUChars, 3);
+ ASSERT_EQ(testAHash3, hasher.hash());
+ ASSERT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testAUChars + 3, 2);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testBUChars, 3);
+ ASSERT_EQ(testBHash3, hasher.hash());
+ ASSERT_EQ(testBHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacters(testBUChars + 3, 2);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharacters(testBUChars, 3);
+ hasher.addCharacters(testBUChars + 3);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+}
+
+TEST(WTF, StringHasher_addCharactersAssumingAligned)
+{
+ StringHasher hasher;
+
+ // Hashing zero characters.
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(static_cast<LChar*>(0), 0);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(nullLChars, 0);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(static_cast<UChar*>(0), 0);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(nullUChars, 0);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(nullUChars);
+ ASSERT_EQ(emptyStringHash, hasher.hash());
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+
+ // Hashing one character.
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(nullLChars, 1);
+ ASSERT_EQ(singleNullCharacterHash, hasher.hash());
+ ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(nullUChars, 1);
+ ASSERT_EQ(singleNullCharacterHash, hasher.hash());
+ ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+
+ // Hashing five characters, all at once.
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testALChars, 5);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testALChars);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testAUChars, 5);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testAUChars);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testBUChars, 5);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testBUChars);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+
+ // Hashing five characters, in groups of two, then the last one.
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testALChars, 2);
+ ASSERT_EQ(testAHash2, hasher.hash());
+ ASSERT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharactersAssumingAligned(testALChars + 2, 2);
+ ASSERT_EQ(testAHash4, hasher.hash());
+ ASSERT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharactersAssumingAligned(testALChars + 4, 1);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testALChars, 2);
+ hasher.addCharactersAssumingAligned(testALChars + 2, 2);
+ hasher.addCharactersAssumingAligned(testALChars + 4);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testAUChars, 2);
+ ASSERT_EQ(testAHash2, hasher.hash());
+ ASSERT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharactersAssumingAligned(testAUChars + 2, 2);
+ ASSERT_EQ(testAHash4, hasher.hash());
+ ASSERT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharactersAssumingAligned(testAUChars + 4, 1);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testAUChars, 2);
+ hasher.addCharactersAssumingAligned(testAUChars + 2, 2);
+ hasher.addCharactersAssumingAligned(testAUChars + 4);
+ ASSERT_EQ(testAHash5, hasher.hash());
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testBUChars, 2);
+ ASSERT_EQ(testBHash2, hasher.hash());
+ ASSERT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharactersAssumingAligned(testBUChars + 2, 2);
+ ASSERT_EQ(testBHash4, hasher.hash());
+ ASSERT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharactersAssumingAligned(testBUChars + 4, 1);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.addCharactersAssumingAligned(testBUChars, 2);
+ hasher.addCharactersAssumingAligned(testBUChars + 2, 2);
+ hasher.addCharactersAssumingAligned(testBUChars + 4);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+
+ // Hashing five characters, first two characters one at a time,
+ // then two more, then the last one.
+ hasher = StringHasher();
+ hasher.addCharacter(testBUChars[0]);
+ ASSERT_EQ(testBHash1, hasher.hash());
+ ASSERT_EQ(testBHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharacter(testBUChars[1]);
+ ASSERT_EQ(testBHash2, hasher.hash());
+ ASSERT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharactersAssumingAligned(testBUChars[2], testBUChars[3]);
+ ASSERT_EQ(testBHash4, hasher.hash());
+ ASSERT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+ hasher.addCharactersAssumingAligned(testBUChars + 4);
+ ASSERT_EQ(testBHash5, hasher.hash());
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked());
+}
+
+TEST(WTF, StringHasher_computeHash)
+{
+ ASSERT_EQ(emptyStringHash, StringHasher::computeHash(static_cast<LChar*>(0), 0));
+ ASSERT_EQ(emptyStringHash, StringHasher::computeHash(nullLChars, 0));
+ ASSERT_EQ(emptyStringHash, StringHasher::computeHash(static_cast<UChar*>(0), 0));
+ ASSERT_EQ(emptyStringHash, StringHasher::computeHash(nullUChars, 0));
+
+ ASSERT_EQ(singleNullCharacterHash, StringHasher::computeHash(nullLChars, 1));
+ ASSERT_EQ(singleNullCharacterHash, StringHasher::computeHash(nullUChars, 1));
+
+ ASSERT_EQ(testAHash5, StringHasher::computeHash(testALChars, 5));
+ ASSERT_EQ(testAHash5, StringHasher::computeHash(testAUChars, 5));
+ ASSERT_EQ(testBHash5, StringHasher::computeHash(testBUChars, 5));
+}
+
+TEST(WTF, StringHasher_computeHashAndMaskTop8Bits)
+{
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(static_cast<LChar*>(0), 0));
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullLChars, 0));
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(static_cast<UChar*>(0), 0));
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullUChars, 0));
+
+ ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullLChars, 1));
+ ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullUChars, 1));
+
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(testALChars, 5));
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(testAUChars, 5));
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(testBUChars, 5));
+}
+
+TEST(WTF, StringHasher_hashMemory)
+{
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory(0, 0));
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory(nullUChars, 0));
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory<0>(0));
+ ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory<0>(nullUChars));
+
+ ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::hashMemory(nullUChars, 2));
+ ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::hashMemory<2>(nullUChars));
+
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, StringHasher::hashMemory(testAUChars, 10));
+ ASSERT_EQ(testAHash5 & 0xFFFFFF, StringHasher::hashMemory<10>(testAUChars));
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, StringHasher::hashMemory(testBUChars, 10));
+ ASSERT_EQ(testBHash5 & 0xFFFFFF, StringHasher::hashMemory<10>(testBUChars));
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp
new file mode 100644
index 000000000..75fadf8c4
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/text/SymbolImpl.h>
+#include <wtf/text/WTFString.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF, StringImplCreationFromLiteral)
+{
+ // Constructor using the template to determine the size.
+ RefPtr<StringImpl> stringWithTemplate = StringImpl::createFromLiteral("Template Literal");
+ ASSERT_EQ(strlen("Template Literal"), stringWithTemplate->length());
+ ASSERT_TRUE(equal(stringWithTemplate.get(), "Template Literal"));
+ ASSERT_TRUE(stringWithTemplate->is8Bit());
+
+ // Constructor taking the size explicitely.
+ const char* programmaticStringData = "Explicit Size Literal";
+ RefPtr<StringImpl> programmaticString = StringImpl::createFromLiteral(programmaticStringData, strlen(programmaticStringData));
+ ASSERT_EQ(strlen(programmaticStringData), programmaticString->length());
+ ASSERT_TRUE(equal(programmaticString.get(), programmaticStringData));
+ ASSERT_EQ(programmaticStringData, reinterpret_cast<const char*>(programmaticString->characters8()));
+ ASSERT_TRUE(programmaticString->is8Bit());
+
+ // Constructor without explicit size.
+ const char* stringWithoutLengthLiteral = "No Size Literal";
+ RefPtr<StringImpl> programmaticStringNoLength = StringImpl::createFromLiteral(stringWithoutLengthLiteral);
+ ASSERT_EQ(strlen(stringWithoutLengthLiteral), programmaticStringNoLength->length());
+ ASSERT_TRUE(equal(programmaticStringNoLength.get(), stringWithoutLengthLiteral));
+ ASSERT_EQ(stringWithoutLengthLiteral, reinterpret_cast<const char*>(programmaticStringNoLength->characters8()));
+ ASSERT_TRUE(programmaticStringNoLength->is8Bit());
+}
+
+TEST(WTF, StringImplReplaceWithLiteral)
+{
+ RefPtr<StringImpl> testStringImpl = StringImpl::createFromLiteral("1224");
+ ASSERT_TRUE(testStringImpl->is8Bit());
+
+ // Cases for 8Bit source.
+ testStringImpl = testStringImpl->replace('2', "", 0);
+ ASSERT_TRUE(equal(testStringImpl.get(), "14"));
+
+ testStringImpl = StringImpl::createFromLiteral("1224");
+ ASSERT_TRUE(testStringImpl->is8Bit());
+
+ testStringImpl = testStringImpl->replace('3', "NotFound", 8);
+ ASSERT_TRUE(equal(testStringImpl.get(), "1224"));
+
+ testStringImpl = testStringImpl->replace('2', "3", 1);
+ ASSERT_TRUE(equal(testStringImpl.get(), "1334"));
+
+ testStringImpl = StringImpl::createFromLiteral("1224");
+ ASSERT_TRUE(testStringImpl->is8Bit());
+ testStringImpl = testStringImpl->replace('2', "555", 3);
+ ASSERT_TRUE(equal(testStringImpl.get(), "15555554"));
+
+ // Cases for 16Bit source.
+ String testString = String::fromUTF8("résumé");
+ ASSERT_FALSE(testString.impl()->is8Bit());
+
+ testStringImpl = testString.impl()->replace('2', "NotFound", 8);
+ ASSERT_TRUE(equal(testStringImpl.get(), String::fromUTF8("résumé").impl()));
+
+ testStringImpl = testString.impl()->replace(UChar(0x00E9 /*U+00E9 is 'é'*/), "e", 1);
+ ASSERT_TRUE(equal(testStringImpl.get(), "resume"));
+
+ testString = String::fromUTF8("résumé");
+ ASSERT_FALSE(testString.impl()->is8Bit());
+ testStringImpl = testString.impl()->replace(UChar(0x00E9 /*U+00E9 is 'é'*/), "", 0);
+ ASSERT_TRUE(equal(testStringImpl.get(), "rsum"));
+
+ testString = String::fromUTF8("résumé");
+ ASSERT_FALSE(testString.impl()->is8Bit());
+ testStringImpl = testString.impl()->replace(UChar(0x00E9 /*U+00E9 is 'é'*/), "555", 3);
+ ASSERT_TRUE(equal(testStringImpl.get(), "r555sum555"));
+}
+
+TEST(WTF, StringImplEqualIgnoringASCIICaseBasic)
+{
+ RefPtr<StringImpl> a = StringImpl::createFromLiteral("aBcDeFG");
+ RefPtr<StringImpl> b = StringImpl::createFromLiteral("ABCDEFG");
+ RefPtr<StringImpl> c = StringImpl::createFromLiteral("abcdefg");
+ const char d[] = "aBcDeFG";
+ RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ RefPtr<StringImpl> shorter = StringImpl::createFromLiteral("abcdef");
+ RefPtr<StringImpl> different = StringImpl::createFromLiteral("abcrefg");
+
+ // Identity.
+ ASSERT_TRUE(equalIgnoringASCIICase(a.get(), a.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(b.get(), b.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(c.get(), c.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(a.get(), d));
+ ASSERT_TRUE(equalIgnoringASCIICase(b.get(), d));
+ ASSERT_TRUE(equalIgnoringASCIICase(c.get(), d));
+
+ // Transitivity.
+ ASSERT_TRUE(equalIgnoringASCIICase(a.get(), b.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(b.get(), c.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(a.get(), c.get()));
+
+ // Negative cases.
+ ASSERT_FALSE(equalIgnoringASCIICase(a.get(), empty.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(b.get(), empty.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(c.get(), empty.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(a.get(), shorter.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(b.get(), shorter.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(c.get(), shorter.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(a.get(), different.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(b.get(), different.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(c.get(), different.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(empty.get(), d));
+ ASSERT_FALSE(equalIgnoringASCIICase(shorter.get(), d));
+ ASSERT_FALSE(equalIgnoringASCIICase(different.get(), d));
+}
+
+TEST(WTF, StringImplEqualIgnoringASCIICaseWithNull)
+{
+ RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
+ ASSERT_FALSE(equalIgnoringASCIICase(nullptr, reference.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(reference.get(), nullptr));
+ ASSERT_TRUE(equalIgnoringASCIICase(nullptr, nullptr));
+}
+
+TEST(WTF, StringImplEqualIgnoringASCIICaseWithEmpty)
+{
+ RefPtr<StringImpl> a = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ RefPtr<StringImpl> b = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ ASSERT_TRUE(equalIgnoringASCIICase(a.get(), b.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(b.get(), a.get()));
+}
+
+static RefPtr<StringImpl> stringFromUTF8(const char* characters)
+{
+ return String::fromUTF8(characters).impl();
+}
+
+TEST(WTF, StringImplEqualIgnoringASCIICaseWithLatin1Characters)
+{
+ RefPtr<StringImpl> a = stringFromUTF8("aBcéeFG");
+ RefPtr<StringImpl> b = stringFromUTF8("ABCÉEFG");
+ RefPtr<StringImpl> c = stringFromUTF8("ABCéEFG");
+ RefPtr<StringImpl> d = stringFromUTF8("abcéefg");
+ const char e[] = "aBcéeFG";
+
+ // Identity.
+ ASSERT_TRUE(equalIgnoringASCIICase(a.get(), a.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(b.get(), b.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(c.get(), c.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(d.get(), d.get()));
+
+ // All combination.
+ ASSERT_FALSE(equalIgnoringASCIICase(a.get(), b.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(a.get(), c.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(a.get(), d.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(b.get(), c.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(b.get(), d.get()));
+ ASSERT_TRUE(equalIgnoringASCIICase(c.get(), d.get()));
+ ASSERT_FALSE(equalIgnoringASCIICase(a.get(), e));
+ ASSERT_FALSE(equalIgnoringASCIICase(b.get(), e));
+ ASSERT_FALSE(equalIgnoringASCIICase(c.get(), e));
+ ASSERT_FALSE(equalIgnoringASCIICase(d.get(), e));
+}
+
+TEST(WTF, StringImplFindIgnoringASCIICaseBasic)
+{
+ RefPtr<StringImpl> referenceA = stringFromUTF8("aBcéeFG");
+ RefPtr<StringImpl> referenceB = stringFromUTF8("ABCÉEFG");
+
+ // Search the exact string.
+ EXPECT_EQ(static_cast<size_t>(0), referenceA->findIgnoringASCIICase(referenceA.get()));
+ EXPECT_EQ(static_cast<size_t>(0), referenceB->findIgnoringASCIICase(referenceB.get()));
+
+ // A and B are distinct by the non-ascii character é/É.
+ EXPECT_EQ(static_cast<size_t>(notFound), referenceA->findIgnoringASCIICase(referenceB.get()));
+ EXPECT_EQ(static_cast<size_t>(notFound), referenceB->findIgnoringASCIICase(referenceA.get()));
+
+ // Find the prefix.
+ EXPECT_EQ(static_cast<size_t>(0), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("a").get()));
+ EXPECT_EQ(static_cast<size_t>(0), referenceA->findIgnoringASCIICase(stringFromUTF8("abcé").get()));
+ EXPECT_EQ(static_cast<size_t>(0), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("A").get()));
+ EXPECT_EQ(static_cast<size_t>(0), referenceA->findIgnoringASCIICase(stringFromUTF8("ABCé").get()));
+ EXPECT_EQ(static_cast<size_t>(0), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("a").get()));
+ EXPECT_EQ(static_cast<size_t>(0), referenceB->findIgnoringASCIICase(stringFromUTF8("abcÉ").get()));
+ EXPECT_EQ(static_cast<size_t>(0), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("A").get()));
+ EXPECT_EQ(static_cast<size_t>(0), referenceB->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get()));
+
+ // Not a prefix.
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("x").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("accé").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("abcÉ").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("X").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("ABDé").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("y").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("accÉ").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("abcé").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("Y").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("ABdÉ").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("ABCé").get()));
+
+ // Find the infix.
+ EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("cée").get()));
+ EXPECT_EQ(static_cast<size_t>(3), referenceA->findIgnoringASCIICase(stringFromUTF8("ée").get()));
+ EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("cé").get()));
+ EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("c").get()));
+ EXPECT_EQ(static_cast<size_t>(3), referenceA->findIgnoringASCIICase(stringFromUTF8("é").get()));
+ EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("Cée").get()));
+ EXPECT_EQ(static_cast<size_t>(3), referenceA->findIgnoringASCIICase(stringFromUTF8("éE").get()));
+ EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("Cé").get()));
+ EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("C").get()));
+
+ EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("cÉe").get()));
+ EXPECT_EQ(static_cast<size_t>(3), referenceB->findIgnoringASCIICase(stringFromUTF8("Ée").get()));
+ EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("cÉ").get()));
+ EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("c").get()));
+ EXPECT_EQ(static_cast<size_t>(3), referenceB->findIgnoringASCIICase(stringFromUTF8("É").get()));
+ EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("CÉe").get()));
+ EXPECT_EQ(static_cast<size_t>(3), referenceB->findIgnoringASCIICase(stringFromUTF8("ÉE").get()));
+ EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("CÉ").get()));
+ EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("C").get()));
+
+ // Not an infix.
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("céd").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("Ée").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("bé").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("x").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("É").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("CÉe").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("éd").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("CÉ").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("Y").get()));
+
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("cée").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("Éc").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("cé").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("W").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("é").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("bÉe").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("éE").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("BÉ").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("z").get()));
+
+ // Find the suffix.
+ EXPECT_EQ(static_cast<size_t>(6), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("g").get()));
+ EXPECT_EQ(static_cast<size_t>(4), referenceA->findIgnoringASCIICase(stringFromUTF8("efg").get()));
+ EXPECT_EQ(static_cast<size_t>(3), referenceA->findIgnoringASCIICase(stringFromUTF8("éefg").get()));
+ EXPECT_EQ(static_cast<size_t>(6), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("G").get()));
+ EXPECT_EQ(static_cast<size_t>(4), referenceA->findIgnoringASCIICase(stringFromUTF8("EFG").get()));
+ EXPECT_EQ(static_cast<size_t>(3), referenceA->findIgnoringASCIICase(stringFromUTF8("éEFG").get()));
+
+ EXPECT_EQ(static_cast<size_t>(6), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("g").get()));
+ EXPECT_EQ(static_cast<size_t>(4), referenceB->findIgnoringASCIICase(stringFromUTF8("efg").get()));
+ EXPECT_EQ(static_cast<size_t>(3), referenceB->findIgnoringASCIICase(stringFromUTF8("Éefg").get()));
+ EXPECT_EQ(static_cast<size_t>(6), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("G").get()));
+ EXPECT_EQ(static_cast<size_t>(4), referenceB->findIgnoringASCIICase(stringFromUTF8("EFG").get()));
+ EXPECT_EQ(static_cast<size_t>(3), referenceB->findIgnoringASCIICase(stringFromUTF8("ÉEFG").get()));
+
+ // Not a suffix.
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("X").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("edg").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("Éefg").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("w").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("dFG").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("ÉEFG").get()));
+
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("Z").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("ffg").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("éefg").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("r").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("EgG").get()));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("éEFG").get()));
+}
+
+TEST(WTF, StringImplFindIgnoringASCIICaseWithValidOffset)
+{
+ RefPtr<StringImpl> reference = stringFromUTF8("ABCÉEFGaBcéeFG");
+ EXPECT_EQ(static_cast<size_t>(0), reference->findIgnoringASCIICase(stringFromUTF8("ABC").get(), 0));
+ EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(stringFromUTF8("ABC").get(), 1));
+ EXPECT_EQ(static_cast<size_t>(0), reference->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get(), 0));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get(), 1));
+ EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(stringFromUTF8("ABCé").get(), 0));
+ EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(stringFromUTF8("ABCé").get(), 1));
+}
+
+TEST(WTF, StringImplFindIgnoringASCIICaseWithInvalidOffset)
+{
+ RefPtr<StringImpl> reference = stringFromUTF8("ABCÉEFGaBcéeFG");
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABC").get(), 15));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABC").get(), 16));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get(), 17));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get(), 42));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get(), std::numeric_limits<unsigned>::max()));
+}
+
+TEST(WTF, StringImplFindIgnoringASCIICaseOnNull)
+{
+ RefPtr<StringImpl> reference = stringFromUTF8("ABCÉEFG");
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, 0));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, 3));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, 7));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, 8));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, 42));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, std::numeric_limits<unsigned>::max()));
+}
+
+TEST(WTF, StringImplFindIgnoringASCIICaseOnEmpty)
+{
+ RefPtr<StringImpl> reference = stringFromUTF8("ABCÉEFG");
+ RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ EXPECT_EQ(static_cast<size_t>(0), reference->findIgnoringASCIICase(empty.get()));
+ EXPECT_EQ(static_cast<size_t>(0), reference->findIgnoringASCIICase(empty.get(), 0));
+ EXPECT_EQ(static_cast<size_t>(3), reference->findIgnoringASCIICase(empty.get(), 3));
+ EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(empty.get(), 7));
+ EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(empty.get(), 8));
+ EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(empty.get(), 42));
+ EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(empty.get(), std::numeric_limits<unsigned>::max()));
+}
+
+TEST(WTF, StringImplFindIgnoringASCIICaseWithPatternLongerThanReference)
+{
+ RefPtr<StringImpl> reference = stringFromUTF8("ABCÉEFG");
+ RefPtr<StringImpl> pattern = stringFromUTF8("XABCÉEFG");
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(pattern.get()));
+ EXPECT_EQ(static_cast<size_t>(1), pattern->findIgnoringASCIICase(reference.get()));
+}
+
+TEST(WTF, StringImplStartsWithIgnoringASCIICaseBasic)
+{
+ RefPtr<StringImpl> reference = stringFromUTF8("aBcéX");
+ RefPtr<StringImpl> referenceEquivalent = stringFromUTF8("AbCéx");
+
+ // Identity.
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(reference.get()));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*reference.get()));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(referenceEquivalent.get()));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*referenceEquivalent.get()));
+ ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(reference.get()));
+ ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(*reference.get()));
+ ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(referenceEquivalent.get()));
+ ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(*referenceEquivalent.get()));
+
+ // Proper prefixes.
+ RefPtr<StringImpl> aLower = StringImpl::createFromLiteral("a");
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(aLower.get()));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*aLower.get()));
+ RefPtr<StringImpl> aUpper = StringImpl::createFromLiteral("A");
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(aUpper.get()));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*aUpper.get()));
+
+ RefPtr<StringImpl> abcLower = StringImpl::createFromLiteral("abc");
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcLower.get()));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcLower.get()));
+ RefPtr<StringImpl> abcUpper = StringImpl::createFromLiteral("ABC");
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcUpper.get()));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcUpper.get()));
+
+ RefPtr<StringImpl> abcAccentLower = stringFromUTF8("abcé");
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcAccentLower.get()));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcAccentLower.get()));
+ RefPtr<StringImpl> abcAccentUpper = stringFromUTF8("ABCé");
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcAccentUpper.get()));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcAccentUpper.get()));
+
+ // Negative cases.
+ RefPtr<StringImpl> differentFirstChar = stringFromUTF8("bBcéX");
+ RefPtr<StringImpl> differentFirstCharProperPrefix = stringFromUTF8("CBcé");
+ ASSERT_FALSE(reference->startsWithIgnoringASCIICase(differentFirstChar.get()));
+ ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*differentFirstChar.get()));
+ ASSERT_FALSE(reference->startsWithIgnoringASCIICase(differentFirstCharProperPrefix.get()));
+ ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*differentFirstCharProperPrefix.get()));
+
+ RefPtr<StringImpl> uppercaseAccent = stringFromUTF8("aBcÉX");
+ RefPtr<StringImpl> uppercaseAccentProperPrefix = stringFromUTF8("aBcÉX");
+ ASSERT_FALSE(reference->startsWithIgnoringASCIICase(uppercaseAccent.get()));
+ ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*uppercaseAccent.get()));
+ ASSERT_FALSE(reference->startsWithIgnoringASCIICase(uppercaseAccentProperPrefix.get()));
+ ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*uppercaseAccentProperPrefix.get()));
+}
+
+TEST(WTF, StringImplStartsWithIgnoringASCIICaseWithNull)
+{
+ RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
+ ASSERT_FALSE(reference->startsWithIgnoringASCIICase(nullptr));
+
+ RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ ASSERT_FALSE(empty->startsWithIgnoringASCIICase(nullptr));
+}
+
+TEST(WTF, StringImplStartsWithIgnoringASCIICaseWithEmpty)
+{
+ RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
+ RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(empty.get()));
+ ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*empty.get()));
+ ASSERT_TRUE(empty->startsWithIgnoringASCIICase(empty.get()));
+ ASSERT_TRUE(empty->startsWithIgnoringASCIICase(*empty.get()));
+ ASSERT_FALSE(empty->startsWithIgnoringASCIICase(reference.get()));
+ ASSERT_FALSE(empty->startsWithIgnoringASCIICase(*reference.get()));
+}
+
+TEST(WTF, StringImplEndsWithIgnoringASCIICaseBasic)
+{
+ RefPtr<StringImpl> reference = stringFromUTF8("XÉCbA");
+ RefPtr<StringImpl> referenceEquivalent = stringFromUTF8("xÉcBa");
+
+ // Identity.
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(reference.get()));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*reference.get()));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(referenceEquivalent.get()));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*referenceEquivalent.get()));
+ ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(reference.get()));
+ ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(*reference.get()));
+ ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(referenceEquivalent.get()));
+ ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(*referenceEquivalent.get()));
+
+ // Proper suffixes.
+ RefPtr<StringImpl> aLower = StringImpl::createFromLiteral("a");
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(aLower.get()));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*aLower.get()));
+ RefPtr<StringImpl> aUpper = StringImpl::createFromLiteral("a");
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(aUpper.get()));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*aUpper.get()));
+
+ RefPtr<StringImpl> abcLower = StringImpl::createFromLiteral("cba");
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcLower.get()));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcLower.get()));
+ RefPtr<StringImpl> abcUpper = StringImpl::createFromLiteral("CBA");
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcUpper.get()));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcUpper.get()));
+
+ RefPtr<StringImpl> abcAccentLower = stringFromUTF8("Écba");
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcAccentLower.get()));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcAccentLower.get()));
+ RefPtr<StringImpl> abcAccentUpper = stringFromUTF8("ÉCBA");
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcAccentUpper.get()));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcAccentUpper.get()));
+
+ // Negative cases.
+ RefPtr<StringImpl> differentLastChar = stringFromUTF8("XÉCbB");
+ RefPtr<StringImpl> differentLastCharProperSuffix = stringFromUTF8("ÉCbb");
+ ASSERT_FALSE(reference->endsWithIgnoringASCIICase(differentLastChar.get()));
+ ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*differentLastChar.get()));
+ ASSERT_FALSE(reference->endsWithIgnoringASCIICase(differentLastCharProperSuffix.get()));
+ ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*differentLastCharProperSuffix.get()));
+
+ RefPtr<StringImpl> lowercaseAccent = stringFromUTF8("aBcéX");
+ RefPtr<StringImpl> loweraseAccentProperSuffix = stringFromUTF8("aBcéX");
+ ASSERT_FALSE(reference->endsWithIgnoringASCIICase(lowercaseAccent.get()));
+ ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*lowercaseAccent.get()));
+ ASSERT_FALSE(reference->endsWithIgnoringASCIICase(loweraseAccentProperSuffix.get()));
+ ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*loweraseAccentProperSuffix.get()));
+}
+
+TEST(WTF, StringImplEndsWithIgnoringASCIICaseWithNull)
+{
+ RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
+ ASSERT_FALSE(reference->endsWithIgnoringASCIICase(nullptr));
+
+ RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ ASSERT_FALSE(empty->endsWithIgnoringASCIICase(nullptr));
+}
+
+TEST(WTF, StringImplEndsWithIgnoringASCIICaseWithEmpty)
+{
+ RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
+ RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(empty.get()));
+ ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*empty.get()));
+ ASSERT_TRUE(empty->endsWithIgnoringASCIICase(empty.get()));
+ ASSERT_TRUE(empty->endsWithIgnoringASCIICase(*empty.get()));
+ ASSERT_FALSE(empty->endsWithIgnoringASCIICase(reference.get()));
+ ASSERT_FALSE(empty->endsWithIgnoringASCIICase(*reference.get()));
+}
+
+TEST(WTF, StringImplCreateSymbolEmpty)
+{
+ RefPtr<StringImpl> reference = StringImpl::createSymbolEmpty();
+ ASSERT_TRUE(reference->isSymbol());
+ ASSERT_FALSE(reference->isAtomic());
+ ASSERT_EQ(0u, reference->length());
+ ASSERT_TRUE(equal(reference.get(), ""));
+}
+
+TEST(WTF, StringImplCreateSymbol)
+{
+ RefPtr<StringImpl> original = stringFromUTF8("original");
+ RefPtr<StringImpl> reference = StringImpl::createSymbol(original);
+ ASSERT_TRUE(reference->isSymbol());
+ ASSERT_FALSE(reference->isAtomic());
+ ASSERT_FALSE(original->isSymbol());
+ ASSERT_FALSE(original->isAtomic());
+ ASSERT_EQ(original->length(), reference->length());
+ ASSERT_TRUE(equal(reference.get(), "original"));
+}
+
+TEST(WTF, StringImplSymbolToAtomicString)
+{
+ RefPtr<StringImpl> original = stringFromUTF8("original");
+ RefPtr<StringImpl> reference = StringImpl::createSymbol(original);
+ ASSERT_TRUE(reference->isSymbol());
+ ASSERT_FALSE(reference->isAtomic());
+
+ RefPtr<StringImpl> atomic = AtomicStringImpl::add(reference.get());
+ ASSERT_TRUE(atomic->isAtomic());
+ ASSERT_FALSE(atomic->isSymbol());
+ ASSERT_TRUE(reference->isSymbol());
+ ASSERT_FALSE(reference->isAtomic());
+}
+
+TEST(WTF, StringImplSymbolEmptyToAtomicString)
+{
+ RefPtr<StringImpl> reference = StringImpl::createSymbolEmpty();
+ ASSERT_TRUE(reference->isSymbol());
+ ASSERT_FALSE(reference->isAtomic());
+
+ RefPtr<StringImpl> atomic = AtomicStringImpl::add(reference.get());
+ ASSERT_TRUE(atomic->isAtomic());
+ ASSERT_FALSE(atomic->isSymbol());
+ ASSERT_TRUE(reference->isSymbol());
+ ASSERT_FALSE(reference->isAtomic());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringOperators.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringOperators.cpp
new file mode 100644
index 000000000..149b85b21
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/StringOperators.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#define WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING() (++wtfStringCopyCount)
+
+static int wtfStringCopyCount;
+
+#include <wtf/text/WTFString.h>
+
+namespace TestWebKitAPI {
+
+#define EXPECT_N_WTF_STRING_COPIES(count, expr) \
+ do { \
+ wtfStringCopyCount = 0; \
+ String __testString = expr; \
+ (void)__testString; \
+ EXPECT_EQ(count, wtfStringCopyCount) << #expr; \
+ } while (false)
+
+TEST(WTF, StringOperators)
+{
+ String string("String");
+ AtomicString atomicString("AtomicString");
+ ASCIILiteral literal("ASCIILiteral");
+
+ EXPECT_EQ(0, wtfStringCopyCount);
+
+ EXPECT_N_WTF_STRING_COPIES(2, string + string);
+ EXPECT_N_WTF_STRING_COPIES(2, string + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + string);
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + atomicString);
+
+ EXPECT_N_WTF_STRING_COPIES(1, "C string" + string);
+ EXPECT_N_WTF_STRING_COPIES(1, string + "C string");
+ EXPECT_N_WTF_STRING_COPIES(1, "C string" + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(1, atomicString + "C string");
+
+ EXPECT_N_WTF_STRING_COPIES(1, literal + string);
+ EXPECT_N_WTF_STRING_COPIES(1, string + literal);
+ EXPECT_N_WTF_STRING_COPIES(1, literal + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(1, atomicString + literal);
+
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + "C string" + string);
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + "C string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + ("C string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, string + "C string" + string + "C string");
+ EXPECT_N_WTF_STRING_COPIES(2, string + ("C string" + string + "C string"));
+ EXPECT_N_WTF_STRING_COPIES(2, (string + "C string") + (string + "C string"));
+
+ EXPECT_N_WTF_STRING_COPIES(2, literal + string + literal + string);
+ EXPECT_N_WTF_STRING_COPIES(2, literal + (string + literal + string));
+ EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + (literal + string));
+ EXPECT_N_WTF_STRING_COPIES(2, string + literal + string + literal);
+ EXPECT_N_WTF_STRING_COPIES(2, string + (literal + string + literal));
+ EXPECT_N_WTF_STRING_COPIES(2, (string + literal) + (string + literal));
+
+ EXPECT_N_WTF_STRING_COPIES(2, literal + string + "C string" + string);
+ EXPECT_N_WTF_STRING_COPIES(2, literal + (string + "C string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + ("C string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + literal + string);
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + literal + string));
+ EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + (literal + string));
+
+ EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + "C string" + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + "C string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + ("C string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + literal + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + literal + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + (literal + atomicString));
+
+ EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + "C string" + string);
+ EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + "C string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + ("C string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + literal + string);
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + literal + string));
+ EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + (literal + string));
+
+ EXPECT_N_WTF_STRING_COPIES(2, literal + string + "C string" + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, literal + (string + "C string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + ("C string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + literal + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + literal + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + (literal + atomicString));
+
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + "C string" + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + "C string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + ("C string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + "C string" + atomicString + "C string");
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + ("C string" + atomicString + "C string"));
+ EXPECT_N_WTF_STRING_COPIES(2, (atomicString + "C string") + (atomicString + "C string"));
+
+ EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + literal + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + literal + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + (literal + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + literal + atomicString + literal);
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + (literal + atomicString + literal));
+ EXPECT_N_WTF_STRING_COPIES(2, (atomicString + literal) + (atomicString + literal));
+
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + "C string" + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + "C string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + ("C string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, string + "C string" + atomicString + "C string");
+ EXPECT_N_WTF_STRING_COPIES(2, string + ("C string" + atomicString + "C string"));
+ EXPECT_N_WTF_STRING_COPIES(2, (string + "C string") + (atomicString + "C string"));
+
+ EXPECT_N_WTF_STRING_COPIES(2, literal + string + literal + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, literal + (string + literal + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + (literal + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, string + literal + atomicString + literal);
+ EXPECT_N_WTF_STRING_COPIES(2, string + (literal + atomicString + literal));
+ EXPECT_N_WTF_STRING_COPIES(2, (string + literal) + (atomicString + literal));
+
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + "C string" + string);
+ EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + "C string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + ("C string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + "C string" + string + "C string");
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + ("C string" + string + "C string"));
+ EXPECT_N_WTF_STRING_COPIES(2, (atomicString + "C string") + (string + "C string"));
+
+ EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + literal + string);
+ EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + literal + string));
+ EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + (literal + string));
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + literal + string + literal);
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + (literal + string + literal));
+ EXPECT_N_WTF_STRING_COPIES(2, (atomicString + literal) + (string + literal));
+
+#if COMPILER(MSVC)
+ EXPECT_N_WTF_STRING_COPIES(1, L"wide string" + string);
+ EXPECT_N_WTF_STRING_COPIES(1, string + L"wide string");
+ EXPECT_N_WTF_STRING_COPIES(1, L"wide string" + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(1, atomicString + L"wide string");
+
+ EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + string + L"wide string" + string);
+ EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + (string + L"wide string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, (L"wide string" + string) + (L"wide string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, string + L"wide string" + string + L"wide string");
+ EXPECT_N_WTF_STRING_COPIES(2, string + (L"wide string" + string + L"wide string"));
+ EXPECT_N_WTF_STRING_COPIES(2, (string + L"wide string") + (string + L"wide string"));
+
+ EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + atomicString + L"wide string" + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + (atomicString + L"wide string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, (L"wide string" + atomicString) + (L"wide string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + L"wide string" + atomicString + L"wide string");
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + (L"wide string" + atomicString + L"wide string"));
+ EXPECT_N_WTF_STRING_COPIES(2, (atomicString + L"wide string") + (atomicString + L"wide string"));
+
+ EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + string + L"wide string" + atomicString);
+ EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + (string + L"wide string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, (L"wide string" + string) + (L"wide string" + atomicString));
+ EXPECT_N_WTF_STRING_COPIES(2, string + L"wide string" + atomicString + L"wide string");
+ EXPECT_N_WTF_STRING_COPIES(2, string + (L"wide string" + atomicString + L"wide string"));
+ EXPECT_N_WTF_STRING_COPIES(2, (string + L"wide string") + (atomicString + L"wide string"));
+
+ EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + atomicString + L"wide string" + string);
+ EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + (atomicString + L"wide string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, (L"wide string" + atomicString) + (L"wide string" + string));
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + L"wide string" + string + L"wide string");
+ EXPECT_N_WTF_STRING_COPIES(2, atomicString + (L"wide string" + string + L"wide string"));
+ EXPECT_N_WTF_STRING_COPIES(2, (atomicString + L"wide string") + (string + L"wide string"));
+#endif
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp
new file mode 100644
index 000000000..408cc642e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp
@@ -0,0 +1,736 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/StringView.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF, StringViewEmptyVsNull)
+{
+ StringView nullView;
+ EXPECT_TRUE(nullView.isNull());
+ EXPECT_TRUE(nullView.isEmpty());
+
+ // Test in a boolean context to test operator bool().
+ if (nullView)
+ FAIL();
+ else
+ SUCCEED();
+
+ if (!nullView)
+ SUCCEED();
+ else
+ FAIL();
+
+ StringView emptyView = StringView::empty();
+ EXPECT_FALSE(emptyView.isNull());
+ EXPECT_TRUE(emptyView.isEmpty());
+
+ // Test in a boolean context to test operator bool().
+ if (emptyView)
+ SUCCEED();
+ else
+ FAIL();
+
+ if (!emptyView)
+ FAIL();
+ else
+ SUCCEED();
+
+ StringView viewWithCharacters(String("hello"));
+ EXPECT_FALSE(viewWithCharacters.isNull());
+ EXPECT_FALSE(viewWithCharacters.isEmpty());
+
+ // Test in a boolean context to test operator bool().
+ if (viewWithCharacters)
+ SUCCEED();
+ else
+ FAIL();
+
+ if (!viewWithCharacters)
+ FAIL();
+ else
+ SUCCEED();
+}
+
+bool compareLoopIterations(StringView::CodePoints codePoints, std::vector<UChar32> expected)
+{
+ std::vector<UChar32> actual;
+ for (auto codePoint : codePoints)
+ actual.push_back(codePoint);
+ return actual == expected;
+}
+
+static bool compareLoopIterations(StringView::CodeUnits codeUnits, std::vector<UChar> expected)
+{
+ std::vector<UChar> actual;
+ for (auto codeUnit : codeUnits)
+ actual.push_back(codeUnit);
+ return actual == expected;
+}
+
+static void build(StringBuilder& builder, std::vector<UChar> input)
+{
+ builder.clear();
+ for (auto codeUnit : input)
+ builder.append(codeUnit);
+}
+
+TEST(WTF, StringViewIterators)
+{
+ compareLoopIterations(StringView().codePoints(), { });
+ compareLoopIterations(StringView().codeUnits(), { });
+
+ compareLoopIterations(StringView::empty().codePoints(), { });
+ compareLoopIterations(StringView::empty().codeUnits(), { });
+
+ compareLoopIterations(StringView(String("hello")).codePoints(), {'h', 'e', 'l', 'l', 'o'});
+ compareLoopIterations(StringView(String("hello")).codeUnits(), {'h', 'e', 'l', 'l', 'o'});
+
+ StringBuilder b;
+ build(b, {0xD800, 0xDD55}); // Surrogates for unicode code point U+10155
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0x10155}));
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800, 0xDD55}));
+
+ build(b, {0xD800}); // Leading surrogate only
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xD800}));
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800}));
+
+ build(b, {0xD800, 0xD801}); // Two leading surrogates
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xD800, 0xD801}));
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800, 0xD801}));
+
+ build(b, {0xDD55}); // Trailing surrogate only
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xDD55}));
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xDD55}));
+
+ build(b, {0xD800, 'h'}); // Leading surrogate followed by non-surrogate
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xD800, 'h'}));
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800, 'h'}));
+
+ build(b, {0x0306}); // "COMBINING BREVE"
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0x0306}));
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0x0306}));
+
+ build(b, {0x0306, 0xD800, 0xDD55, 'h', 'e', 'l', 'o'}); // Mix of single code unit and multi code unit code points
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0x0306, 0x10155, 'h', 'e', 'l', 'o'}));
+ EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0x0306, 0xD800, 0xDD55, 'h', 'e', 'l', 'o'}));
+}
+
+TEST(WTF, StringViewEqualIgnoringASCIICaseBasic)
+{
+ RefPtr<StringImpl> a = StringImpl::createFromLiteral("aBcDeFG");
+ RefPtr<StringImpl> b = StringImpl::createFromLiteral("ABCDEFG");
+ RefPtr<StringImpl> c = StringImpl::createFromLiteral("abcdefg");
+ const char d[] = "aBcDeFG";
+ RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ RefPtr<StringImpl> shorter = StringImpl::createFromLiteral("abcdef");
+ RefPtr<StringImpl> different = StringImpl::createFromLiteral("abcrefg");
+
+ StringView stringViewA(*a.get());
+ StringView stringViewB(*b.get());
+ StringView stringViewC(*c.get());
+ StringView emptyStringView(*empty.get());
+ StringView shorterStringView(*shorter.get());
+ StringView differentStringView(*different.get());
+
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewB));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewC));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewC));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, d));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, d));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, d));
+
+ // Identity.
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewA));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewB));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, stringViewC));
+
+ // Transitivity.
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewB));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewC));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewC));
+
+ // Negative cases.
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, emptyStringView));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, emptyStringView));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewC, emptyStringView));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, shorterStringView));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, shorterStringView));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewC, shorterStringView));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, differentStringView));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, differentStringView));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewC, differentStringView));
+ ASSERT_FALSE(equalIgnoringASCIICase(emptyStringView, d));
+ ASSERT_FALSE(equalIgnoringASCIICase(shorterStringView, d));
+ ASSERT_FALSE(equalIgnoringASCIICase(differentStringView, d));
+}
+
+TEST(WTF, StringViewEqualIgnoringASCIICaseWithEmpty)
+{
+ RefPtr<StringImpl> a = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ RefPtr<StringImpl> b = StringImpl::create(reinterpret_cast<const LChar*>(""));
+ StringView stringViewA(*a.get());
+ StringView stringViewB(*b.get());
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewB));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewA));
+}
+
+TEST(WTF, StringViewEqualIgnoringASCIICaseWithLatin1Characters)
+{
+ RefPtr<StringImpl> a = StringImpl::create(reinterpret_cast<const LChar*>("aBcéeFG"));
+ RefPtr<StringImpl> b = StringImpl::create(reinterpret_cast<const LChar*>("ABCÉEFG"));
+ RefPtr<StringImpl> c = StringImpl::create(reinterpret_cast<const LChar*>("ABCéEFG"));
+ RefPtr<StringImpl> d = StringImpl::create(reinterpret_cast<const LChar*>("abcéefg"));
+ const char e[] = "aBcéeFG";
+ StringView stringViewA(*a.get());
+ StringView stringViewB(*b.get());
+ StringView stringViewC(*c.get());
+ StringView stringViewD(*d.get());
+
+ // Identity.
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewA));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewB));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, stringViewC));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewD, stringViewD));
+
+ // All combination.
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, stringViewB));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewC));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewD));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, stringViewC));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, stringViewD));
+ ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, stringViewD));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, e));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, e));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewC, e));
+ ASSERT_FALSE(equalIgnoringASCIICase(stringViewD, e));
+}
+
+StringView stringViewFromLiteral(const char* characters)
+{
+ return StringView(reinterpret_cast<const LChar*>(characters), strlen(characters));
+}
+
+StringView stringViewFromUTF8(String &ref, const char* characters)
+{
+ ref = String::fromUTF8(characters);
+ return ref;
+}
+
+TEST(WTF, StringViewFindIgnoringASCIICaseBasic)
+{
+ String referenceAHolder;
+ StringView referenceA = stringViewFromUTF8(referenceAHolder, "aBcéeFG");
+ String referenceBHolder;
+ StringView referenceB = stringViewFromUTF8(referenceBHolder, "ABCÉEFG");
+
+ // Search the exact string.
+ EXPECT_EQ(static_cast<size_t>(0), referenceA.findIgnoringASCIICase(referenceA));
+ EXPECT_EQ(static_cast<size_t>(0), referenceB.findIgnoringASCIICase(referenceB));
+
+ // A and B are distinct by the non-ascii character é/É.
+ EXPECT_EQ(static_cast<size_t>(notFound), referenceA.findIgnoringASCIICase(referenceB));
+ EXPECT_EQ(static_cast<size_t>(notFound), referenceB.findIgnoringASCIICase(referenceA));
+
+ String tempStringHolder;
+ // Find the prefix.
+ EXPECT_EQ(static_cast<size_t>(0), referenceA.findIgnoringASCIICase(stringViewFromLiteral("a")));
+ EXPECT_EQ(static_cast<size_t>(0), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "abcé")));
+ EXPECT_EQ(static_cast<size_t>(0), referenceA.findIgnoringASCIICase(stringViewFromLiteral("A")));
+ EXPECT_EQ(static_cast<size_t>(0), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCé")));
+ EXPECT_EQ(static_cast<size_t>(0), referenceB.findIgnoringASCIICase(stringViewFromLiteral("a")));
+ EXPECT_EQ(static_cast<size_t>(0), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "abcÉ")));
+ EXPECT_EQ(static_cast<size_t>(0), referenceB.findIgnoringASCIICase(stringViewFromLiteral("A")));
+ EXPECT_EQ(static_cast<size_t>(0), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ")));
+
+ // Not a prefix.
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromLiteral("x")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "accé")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "abcÉ")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromLiteral("X")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABDé")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromLiteral("y")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "accÉ")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "abcé")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromLiteral("Y")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABdÉ")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCé")));
+
+ // Find the infix.
+ EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cée")));
+ EXPECT_EQ(static_cast<size_t>(3), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ée")));
+ EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cé")));
+ EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "c")));
+ EXPECT_EQ(static_cast<size_t>(3), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "é")));
+ EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Cée")));
+ EXPECT_EQ(static_cast<size_t>(3), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éE")));
+ EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Cé")));
+ EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "C")));
+
+ EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cÉe")));
+ EXPECT_EQ(static_cast<size_t>(3), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Ée")));
+ EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cÉ")));
+ EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "c")));
+ EXPECT_EQ(static_cast<size_t>(3), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "É")));
+ EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "CÉe")));
+ EXPECT_EQ(static_cast<size_t>(3), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ÉE")));
+ EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "CÉ")));
+ EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "C")));
+
+ // Not an infix.
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "céd")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Ée")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "bé")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "x")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "É")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "CÉe")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éd")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "CÉ")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Y")));
+
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cée")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Éc")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cé")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "W")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "é")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "bÉe")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éE")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "BÉ")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "z")));
+
+ // Find the suffix.
+ EXPECT_EQ(static_cast<size_t>(6), referenceA.findIgnoringASCIICase(stringViewFromLiteral("g")));
+ EXPECT_EQ(static_cast<size_t>(4), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "efg")));
+ EXPECT_EQ(static_cast<size_t>(3), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éefg")));
+ EXPECT_EQ(static_cast<size_t>(6), referenceA.findIgnoringASCIICase(stringViewFromLiteral("G")));
+ EXPECT_EQ(static_cast<size_t>(4), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "EFG")));
+ EXPECT_EQ(static_cast<size_t>(3), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éEFG")));
+
+ EXPECT_EQ(static_cast<size_t>(6), referenceB.findIgnoringASCIICase(stringViewFromLiteral("g")));
+ EXPECT_EQ(static_cast<size_t>(4), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "efg")));
+ EXPECT_EQ(static_cast<size_t>(3), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Éefg")));
+ EXPECT_EQ(static_cast<size_t>(6), referenceB.findIgnoringASCIICase(stringViewFromLiteral("G")));
+ EXPECT_EQ(static_cast<size_t>(4), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "EFG")));
+ EXPECT_EQ(static_cast<size_t>(3), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ÉEFG")));
+
+ // Not a suffix.
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromLiteral("X")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "edg")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Éefg")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromLiteral("w")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "dFG")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ÉEFG")));
+
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromLiteral("Z")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ffg")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éefg")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromLiteral("r")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "EgG")));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éEFG")));
+}
+
+TEST(WTF, StringViewFindIgnoringASCIICaseWithValidOffset)
+{
+ String referenceHolder;
+ StringView reference = stringViewFromUTF8(referenceHolder, "ABCÉEFGaBcéeFG");
+ String tempStringHolder;
+
+ EXPECT_EQ(static_cast<size_t>(0), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABC"), 0));
+ EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABC"), 1));
+ EXPECT_EQ(static_cast<size_t>(0), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"), 0));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"), 1));
+ EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCé"), 0));
+ EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCé"), 1));
+}
+
+TEST(WTF, StringViewFindIgnoringASCIICaseWithInvalidOffset)
+{
+ String referenceHolder;
+ StringView reference = stringViewFromUTF8(referenceHolder, "ABCÉEFGaBcéeFG");
+ String tempStringHolder;
+
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABC"), 15));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABC"), 16));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"), 17));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"), 42));
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"), std::numeric_limits<unsigned>::max()));
+}
+
+TEST(WTF, StringViewFindIgnoringASCIICaseOnEmpty)
+{
+ String referenceHolder;
+ StringView reference = stringViewFromUTF8(referenceHolder, "ABCÉEFG");
+ StringView empty = stringViewFromLiteral("");
+ EXPECT_EQ(static_cast<size_t>(0), reference.findIgnoringASCIICase(empty));
+ EXPECT_EQ(static_cast<size_t>(0), reference.findIgnoringASCIICase(empty, 0));
+ EXPECT_EQ(static_cast<size_t>(3), reference.findIgnoringASCIICase(empty, 3));
+ EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(empty, 7));
+ EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(empty, 8));
+ EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(empty, 42));
+ EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(empty, std::numeric_limits<unsigned>::max()));
+}
+
+TEST(WTF, StringViewFindIgnoringASCIICaseWithPatternLongerThanReference)
+{
+ String referenceHolder;
+ StringView reference = stringViewFromUTF8(referenceHolder, "ABCÉEFG");
+ String patternHolder;
+ StringView pattern = stringViewFromUTF8(patternHolder, "ABCÉEFGA");
+
+ EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(pattern));
+ EXPECT_EQ(static_cast<size_t>(0), pattern.findIgnoringASCIICase(reference));
+}
+
+TEST(WTF, StringViewStartsWithBasic)
+{
+ StringView reference = stringViewFromLiteral("abcdefg");
+ String referenceUTF8Ref;
+ StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "àîûèô");
+
+ StringView oneLetterPrefix = stringViewFromLiteral("a");
+ StringView shortPrefix = stringViewFromLiteral("abc");
+ StringView longPrefix = stringViewFromLiteral("abcdef");
+ StringView upperCasePrefix = stringViewFromLiteral("ABC");
+ StringView empty = stringViewFromLiteral("");
+ StringView notPrefix = stringViewFromLiteral("bc");
+
+ String oneLetterPrefixUTF8Ref;
+ StringView oneLetterPrefixUTF8 = stringViewFromUTF8(oneLetterPrefixUTF8Ref, "à");
+ String shortPrefixUTF8Ref;
+ StringView shortPrefixUTF8 = stringViewFromUTF8(shortPrefixUTF8Ref, "àî");
+ String longPrefixUTF8Ref;
+ StringView longPrefixUTF8 = stringViewFromUTF8(longPrefixUTF8Ref, "àîûè");
+ String upperCasePrefixUTF8Ref;
+ StringView upperCasePrefixUTF8 = stringViewFromUTF8(upperCasePrefixUTF8Ref, "ÀÎ");
+ String notPrefixUTF8Ref;
+ StringView notPrefixUTF8 = stringViewFromUTF8(notPrefixUTF8Ref, "îû");
+
+ EXPECT_TRUE(reference.startsWith(reference));
+ EXPECT_TRUE(reference.startsWith(oneLetterPrefix));
+ EXPECT_TRUE(reference.startsWith(shortPrefix));
+ EXPECT_TRUE(reference.startsWith(longPrefix));
+ EXPECT_TRUE(reference.startsWith(empty));
+
+ EXPECT_TRUE(referenceUTF8.startsWith(referenceUTF8));
+ EXPECT_TRUE(referenceUTF8.startsWith(oneLetterPrefixUTF8));
+ EXPECT_TRUE(referenceUTF8.startsWith(shortPrefixUTF8));
+ EXPECT_TRUE(referenceUTF8.startsWith(longPrefixUTF8));
+ EXPECT_TRUE(referenceUTF8.startsWith(empty));
+
+ EXPECT_FALSE(reference.startsWith(notPrefix));
+ EXPECT_FALSE(reference.startsWith(upperCasePrefix));
+ EXPECT_FALSE(reference.startsWith(notPrefixUTF8));
+ EXPECT_FALSE(reference.startsWith(upperCasePrefixUTF8));
+ EXPECT_FALSE(referenceUTF8.startsWith(notPrefix));
+ EXPECT_FALSE(referenceUTF8.startsWith(upperCasePrefix));
+ EXPECT_FALSE(referenceUTF8.startsWith(notPrefixUTF8));
+ EXPECT_FALSE(referenceUTF8.startsWith(upperCasePrefixUTF8));
+}
+
+TEST(WTF, StringViewStartsWithEmpty)
+{
+ StringView a = stringViewFromLiteral("");
+ String refB;
+ StringView b = stringViewFromUTF8(refB, "");
+
+ EXPECT_TRUE(a.startsWith(a));
+ EXPECT_TRUE(a.startsWith(b));
+ EXPECT_TRUE(b.startsWith(a));
+ EXPECT_TRUE(b.startsWith(b));
+}
+
+TEST(WTF, StringViewStartsWithIgnoringASCIICaseBasic)
+{
+ StringView reference = stringViewFromLiteral("abcdefg");
+
+ String referenceUTF8Ref;
+ StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "àîûèô");
+
+ StringView oneLetterPrefix = stringViewFromLiteral("a");
+ StringView shortPrefix = stringViewFromLiteral("abc");
+ StringView longPrefix = stringViewFromLiteral("abcdef");
+ StringView upperCasePrefix = stringViewFromLiteral("ABC");
+ StringView mixedCasePrefix = stringViewFromLiteral("aBcDe");
+ StringView empty = stringViewFromLiteral("");
+ StringView notPrefix = stringViewFromLiteral("bc");
+
+ String oneLetterPrefixUTF8Ref;
+ StringView oneLetterPrefixUTF8 = stringViewFromUTF8(oneLetterPrefixUTF8Ref, "à");
+ String shortPrefixUTF8Ref;
+ StringView shortPrefixUTF8 = stringViewFromUTF8(shortPrefixUTF8Ref, "àî");
+ String longPrefixUTF8Ref;
+ StringView longPrefixUTF8 = stringViewFromUTF8(longPrefixUTF8Ref, "àîûè");
+ String upperCasePrefixUTF8Ref;
+ StringView upperCasePrefixUTF8 = stringViewFromUTF8(upperCasePrefixUTF8Ref, "ÀÎ");
+ String notPrefixUTF8Ref;
+ StringView notPrefixUTF8 = stringViewFromUTF8(notPrefixUTF8Ref, "îû");
+
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(reference));
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(oneLetterPrefix));
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(shortPrefix));
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(longPrefix));
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(empty));
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(upperCasePrefix));
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(mixedCasePrefix));
+
+ EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(referenceUTF8));
+ EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(oneLetterPrefixUTF8));
+ EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(shortPrefixUTF8));
+ EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(longPrefixUTF8));
+ EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(empty));
+
+ EXPECT_FALSE(reference.startsWithIgnoringASCIICase(notPrefix));
+ EXPECT_FALSE(reference.startsWithIgnoringASCIICase(notPrefixUTF8));
+ EXPECT_FALSE(reference.startsWithIgnoringASCIICase(upperCasePrefixUTF8));
+ EXPECT_FALSE(referenceUTF8.startsWithIgnoringASCIICase(notPrefix));
+ EXPECT_FALSE(referenceUTF8.startsWithIgnoringASCIICase(notPrefixUTF8));
+ EXPECT_FALSE(referenceUTF8.startsWithIgnoringASCIICase(upperCasePrefix));
+ EXPECT_FALSE(referenceUTF8.startsWithIgnoringASCIICase(upperCasePrefixUTF8));
+}
+
+
+TEST(WTF, StringViewStartsWithIgnoringASCIICaseEmpty)
+{
+ StringView a = stringViewFromLiteral("");
+ String refB;
+ StringView b = stringViewFromUTF8(refB, "");
+
+ EXPECT_TRUE(a.startsWithIgnoringASCIICase(a));
+ EXPECT_TRUE(a.startsWithIgnoringASCIICase(b));
+ EXPECT_TRUE(b.startsWithIgnoringASCIICase(a));
+ EXPECT_TRUE(b.startsWithIgnoringASCIICase(b));
+}
+
+TEST(WTF, StringViewStartsWithIgnoringASCIICaseWithLatin1Characters)
+{
+ StringView reference = stringViewFromLiteral("aBcéeFG");
+ String referenceUTF8Ref;
+ StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "aBcéeFG");
+
+ StringView a = stringViewFromLiteral("aBcéeF");
+ StringView b = stringViewFromLiteral("ABCéEF");
+ StringView c = stringViewFromLiteral("abcéef");
+ StringView d = stringViewFromLiteral("Abcéef");
+
+ String refE;
+ StringView e = stringViewFromUTF8(refE, "aBcéeF");
+ String refF;
+ StringView f = stringViewFromUTF8(refF, "ABCéEF");
+ String refG;
+ StringView g = stringViewFromUTF8(refG, "abcéef");
+ String refH;
+ StringView h = stringViewFromUTF8(refH, "Abcéef");
+
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(a));
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(b));
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(c));
+ EXPECT_TRUE(reference.startsWithIgnoringASCIICase(d));
+
+ EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(e));
+ EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(f));
+ EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(g));
+ EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(h));
+
+ EXPECT_FALSE(reference.endsWithIgnoringASCIICase(referenceUTF8));
+ EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(reference));
+}
+
+TEST(WTF, StringViewEndsWithBasic)
+{
+ StringView reference = stringViewFromLiteral("abcdefg");
+ String referenceUTF8Ref;
+ StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "àîûèô");
+
+ StringView oneLetterSuffix = stringViewFromLiteral("g");
+ StringView shortSuffix = stringViewFromLiteral("efg");
+ StringView longSuffix = stringViewFromLiteral("cdefg");
+ StringView upperCaseSuffix = stringViewFromLiteral("EFG");
+ StringView empty = stringViewFromLiteral("");
+ StringView notSuffix = stringViewFromLiteral("bc");
+
+ String oneLetterSuffixUTF8Ref;
+ StringView oneLetterSuffixUTF8 = stringViewFromUTF8(oneLetterSuffixUTF8Ref, "ô");
+ String shortSuffixUTF8Ref;
+ StringView shortSuffixUTF8 = stringViewFromUTF8(shortSuffixUTF8Ref, "èô");
+ String longSuffixUTF8Ref;
+ StringView longSuffixUTF8 = stringViewFromUTF8(longSuffixUTF8Ref, "îûèô");
+ String upperCaseSuffixUTF8Ref;
+ StringView upperCaseSuffixUTF8 = stringViewFromUTF8(upperCaseSuffixUTF8Ref, "ÈÔ");
+ String notSuffixUTF8Ref;
+ StringView notSuffixUTF8 = stringViewFromUTF8(notSuffixUTF8Ref, "îû");
+
+ EXPECT_TRUE(reference.endsWith(reference));
+ EXPECT_TRUE(reference.endsWith(oneLetterSuffix));
+ EXPECT_TRUE(reference.endsWith(shortSuffix));
+ EXPECT_TRUE(reference.endsWith(longSuffix));
+ EXPECT_TRUE(reference.endsWith(empty));
+
+ EXPECT_TRUE(referenceUTF8.endsWith(referenceUTF8));
+ EXPECT_TRUE(referenceUTF8.endsWith(oneLetterSuffixUTF8));
+ EXPECT_TRUE(referenceUTF8.endsWith(shortSuffixUTF8));
+ EXPECT_TRUE(referenceUTF8.endsWith(longSuffixUTF8));
+ EXPECT_TRUE(referenceUTF8.endsWith(empty));
+
+ EXPECT_FALSE(reference.endsWith(notSuffix));
+ EXPECT_FALSE(reference.endsWith(upperCaseSuffix));
+ EXPECT_FALSE(reference.endsWith(notSuffixUTF8));
+ EXPECT_FALSE(reference.endsWith(upperCaseSuffixUTF8));
+ EXPECT_FALSE(referenceUTF8.endsWith(notSuffix));
+ EXPECT_FALSE(referenceUTF8.endsWith(upperCaseSuffix));
+ EXPECT_FALSE(referenceUTF8.endsWith(notSuffixUTF8));
+ EXPECT_FALSE(referenceUTF8.endsWith(upperCaseSuffixUTF8));
+}
+
+TEST(WTF, StringViewEndsWithEmpty)
+{
+ StringView a = stringViewFromLiteral("");
+ String refB;
+ StringView b = stringViewFromUTF8(refB, "");
+
+ EXPECT_TRUE(a.endsWith(a));
+ EXPECT_TRUE(a.endsWith(b));
+ EXPECT_TRUE(b.endsWith(a));
+ EXPECT_TRUE(b.endsWith(b));
+}
+
+TEST(WTF, StringViewEndsWithIgnoringASCIICaseBasic)
+{
+ StringView reference = stringViewFromLiteral("abcdefg");
+ String referenceUTF8Ref;
+ StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "àîûèô");
+
+ StringView oneLetterSuffix = stringViewFromLiteral("g");
+ StringView shortSuffix = stringViewFromLiteral("efg");
+ StringView longSuffix = stringViewFromLiteral("bcdefg");
+ StringView upperCaseSuffix = stringViewFromLiteral("EFG");
+ StringView mixedCaseSuffix = stringViewFromLiteral("bCdeFg");
+ StringView empty = stringViewFromLiteral("");
+ StringView notSuffix = stringViewFromLiteral("bc");
+
+ String oneLetterSuffixUTF8Ref;
+ StringView oneLetterSuffixUTF8 = stringViewFromUTF8(oneLetterSuffixUTF8Ref, "ô");
+ String shortSuffixUTF8Ref;
+ StringView shortSuffixUTF8 = stringViewFromUTF8(shortSuffixUTF8Ref, "èô");
+ String longSuffixUTF8Ref;
+ StringView longSuffixUTF8 = stringViewFromUTF8(longSuffixUTF8Ref, "îûèô");
+ String upperCaseSuffixUTF8Ref;
+ StringView upperCaseSuffixUTF8 = stringViewFromUTF8(upperCaseSuffixUTF8Ref, "ÈÔ");
+ String notSuffixUTF8Ref;
+ StringView notSuffixUTF8 = stringViewFromUTF8(notSuffixUTF8Ref, "îû");
+
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(reference));
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(oneLetterSuffix));
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(shortSuffix));
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(longSuffix));
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(empty));
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(upperCaseSuffix));
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(mixedCaseSuffix));
+
+ EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(referenceUTF8));
+ EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(oneLetterSuffixUTF8));
+ EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(shortSuffixUTF8));
+ EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(longSuffixUTF8));
+ EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(empty));
+
+ EXPECT_FALSE(reference.endsWithIgnoringASCIICase(notSuffix));
+ EXPECT_FALSE(reference.endsWithIgnoringASCIICase(notSuffixUTF8));
+ EXPECT_FALSE(reference.endsWithIgnoringASCIICase(upperCaseSuffixUTF8));
+ EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(notSuffix));
+ EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(notSuffixUTF8));
+ EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(upperCaseSuffix));
+ EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(upperCaseSuffixUTF8));
+}
+
+TEST(WTF, StringViewEndsWithIgnoringASCIICaseEmpty)
+{
+ StringView a = stringViewFromLiteral("");
+ String refB;
+ StringView b = stringViewFromUTF8(refB, "");
+
+ EXPECT_TRUE(a.endsWithIgnoringASCIICase(a));
+ EXPECT_TRUE(a.endsWithIgnoringASCIICase(b));
+ EXPECT_TRUE(b.endsWithIgnoringASCIICase(a));
+ EXPECT_TRUE(b.endsWithIgnoringASCIICase(b));
+}
+
+TEST(WTF, StringViewEndsWithIgnoringASCIICaseWithLatin1Characters)
+{
+ StringView reference = stringViewFromLiteral("aBcéeFG");
+ String referenceUTF8Ref;
+ StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "aBcéeFG");
+
+ StringView a = stringViewFromLiteral("BcéeFG");
+ StringView b = stringViewFromLiteral("BCéEFG");
+ StringView c = stringViewFromLiteral("bcéefG");
+ StringView d = stringViewFromLiteral("bcéefg");
+
+ String refE;
+ StringView e = stringViewFromUTF8(refE, "bcéefG");
+ String refF;
+ StringView f = stringViewFromUTF8(refF, "BCéEFG");
+ String refG;
+ StringView g = stringViewFromUTF8(refG, "bcéefG");
+ String refH;
+ StringView h = stringViewFromUTF8(refH, "bcéefg");
+
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(a));
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(b));
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(c));
+ EXPECT_TRUE(reference.endsWithIgnoringASCIICase(d));
+
+ EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(e));
+ EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(f));
+ EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(g));
+ EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(h));
+
+ EXPECT_FALSE(reference.endsWithIgnoringASCIICase(referenceUTF8));
+ EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(reference));
+}
+
+TEST(WTF, StringView8Bit)
+{
+ StringView nullView;
+ StringView emptyView = StringView::empty();
+ EXPECT_TRUE(StringView().is8Bit());
+ EXPECT_TRUE(StringView::empty().is8Bit());
+
+ LChar* lcharPtr = nullptr;
+ UChar* ucharPtr = nullptr;
+ EXPECT_TRUE(StringView(lcharPtr, 0).is8Bit());
+ EXPECT_FALSE(StringView(ucharPtr, 0).is8Bit());
+
+ EXPECT_TRUE(StringView(String(lcharPtr, 0)).is8Bit());
+ EXPECT_TRUE(StringView(String(ucharPtr, 0)).is8Bit());
+
+ EXPECT_TRUE(StringView(String().impl()).is8Bit());
+ EXPECT_TRUE(StringView(emptyString().impl()).is8Bit());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/TemporaryChange.cpp b/Tools/TestWebKitAPI/Tests/WTF/TemporaryChange.cpp
new file mode 100644
index 000000000..3ba0f15bd
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/TemporaryChange.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/TemporaryChange.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF, TemporaryChangeNested)
+{
+ bool originallyFalse = false;
+ {
+ TemporaryChange<bool> change1OriginallyFalse(originallyFalse, true);
+ EXPECT_TRUE(originallyFalse);
+ {
+ TemporaryChange<bool> change2OriginallyFalse(originallyFalse, false);
+ EXPECT_FALSE(originallyFalse);
+ }
+ EXPECT_TRUE(originallyFalse);
+ }
+ EXPECT_FALSE(originallyFalse);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp b/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp
new file mode 100644
index 000000000..bec224141
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp
@@ -0,0 +1,617 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "MoveOnly.h"
+#include <wtf/Vector.h>
+#include <wtf/text/CString.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF_Vector, Basic)
+{
+ Vector<int> intVector;
+ EXPECT_TRUE(intVector.isEmpty());
+ EXPECT_EQ(0U, intVector.size());
+ EXPECT_EQ(0U, intVector.capacity());
+}
+
+TEST(WTF_Vector, Iterator)
+{
+ Vector<int> intVector;
+ intVector.append(10);
+ intVector.append(11);
+ intVector.append(12);
+ intVector.append(13);
+
+ Vector<int>::iterator it = intVector.begin();
+ Vector<int>::iterator end = intVector.end();
+ EXPECT_TRUE(end != it);
+
+ EXPECT_EQ(10, *it);
+ ++it;
+ EXPECT_EQ(11, *it);
+ ++it;
+ EXPECT_EQ(12, *it);
+ ++it;
+ EXPECT_EQ(13, *it);
+ ++it;
+
+ EXPECT_TRUE(end == it);
+}
+
+TEST(WTF_Vector, OverloadedOperatorAmpersand)
+{
+ struct Test {
+ private:
+ Test* operator&();
+ };
+
+ Vector<Test> vector;
+ vector.append(Test());
+}
+
+TEST(WTF_Vector, AppendLast)
+{
+ Vector<unsigned> vector;
+ vector.append(0);
+
+ // FIXME: This test needs to be run with GuardMalloc to show the bug.
+ for (size_t i = 0; i < 100; ++i)
+ vector.append(const_cast<const unsigned&>(vector.last()));
+}
+
+TEST(WTF_Vector, InitializerList)
+{
+ Vector<int> vector = { 1, 2, 3, 4 };
+ EXPECT_EQ(4U, vector.size());
+
+ EXPECT_EQ(1, vector[0]);
+ EXPECT_EQ(2, vector[1]);
+ EXPECT_EQ(3, vector[2]);
+ EXPECT_EQ(4, vector[3]);
+}
+
+TEST(WTF_Vector, InitializeFromOtherInitialCapacity)
+{
+ Vector<int, 3> vector = { 1, 3, 2, 4 };
+ Vector<int, 5> vectorCopy(vector);
+ EXPECT_EQ(4U, vector.size());
+ EXPECT_EQ(4U, vectorCopy.size());
+ EXPECT_EQ(5U, vectorCopy.capacity());
+
+ EXPECT_EQ(1, vectorCopy[0]);
+ EXPECT_EQ(3, vectorCopy[1]);
+ EXPECT_EQ(2, vectorCopy[2]);
+ EXPECT_EQ(4, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, CopyFromOtherInitialCapacity)
+{
+ Vector<int, 3> vector = { 1, 3, 2, 4 };
+ Vector<int, 5> vectorCopy { 0 };
+ EXPECT_EQ(4U, vector.size());
+ EXPECT_EQ(1U, vectorCopy.size());
+
+ vectorCopy = vector;
+
+ EXPECT_EQ(4U, vector.size());
+ EXPECT_EQ(4U, vectorCopy.size());
+ EXPECT_EQ(5U, vectorCopy.capacity());
+
+ EXPECT_EQ(1, vectorCopy[0]);
+ EXPECT_EQ(3, vectorCopy[1]);
+ EXPECT_EQ(2, vectorCopy[2]);
+ EXPECT_EQ(4, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, InitializeFromOtherOverflowBehavior)
+{
+ Vector<int, 7, WTF::CrashOnOverflow> vector = { 4, 3, 2, 1 };
+ Vector<int, 7, UnsafeVectorOverflow> vectorCopy(vector);
+ EXPECT_EQ(4U, vector.size());
+ EXPECT_EQ(4U, vectorCopy.size());
+
+ EXPECT_EQ(4, vectorCopy[0]);
+ EXPECT_EQ(3, vectorCopy[1]);
+ EXPECT_EQ(2, vectorCopy[2]);
+ EXPECT_EQ(1, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, CopyFromOtherOverflowBehavior)
+{
+ Vector<int, 7, WTF::CrashOnOverflow> vector = { 4, 3, 2, 1 };
+ Vector<int, 7, UnsafeVectorOverflow> vectorCopy = { 0, 0, 0 };
+
+ EXPECT_EQ(4U, vector.size());
+ EXPECT_EQ(3U, vectorCopy.size());
+
+ vectorCopy = vector;
+
+ EXPECT_EQ(4U, vector.size());
+ EXPECT_EQ(4U, vectorCopy.size());
+
+ EXPECT_EQ(4, vectorCopy[0]);
+ EXPECT_EQ(3, vectorCopy[1]);
+ EXPECT_EQ(2, vectorCopy[2]);
+ EXPECT_EQ(1, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, InitializeFromOtherMinCapacity)
+{
+ Vector<int, 7, WTF::CrashOnOverflow, 1> vector = { 3, 4, 2, 1 };
+ Vector<int, 7, WTF::CrashOnOverflow, 50> vectorCopy(vector);
+ EXPECT_EQ(4U, vector.size());
+ EXPECT_EQ(4U, vectorCopy.size());
+
+ EXPECT_EQ(3, vectorCopy[0]);
+ EXPECT_EQ(4, vectorCopy[1]);
+ EXPECT_EQ(2, vectorCopy[2]);
+ EXPECT_EQ(1, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, CopyFromOtherMinCapacity)
+{
+ Vector<int, 7, WTF::CrashOnOverflow, 1> vector = { 3, 4, 2, 1 };
+ Vector<int, 7, WTF::CrashOnOverflow, 50> vectorCopy;
+
+ EXPECT_EQ(4U, vector.size());
+ EXPECT_EQ(0U, vectorCopy.size());
+
+ vectorCopy = vector;
+
+ EXPECT_EQ(4U, vector.size());
+ EXPECT_EQ(4U, vectorCopy.size());
+
+ EXPECT_EQ(3, vectorCopy[0]);
+ EXPECT_EQ(4, vectorCopy[1]);
+ EXPECT_EQ(2, vectorCopy[2]);
+ EXPECT_EQ(1, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, Reverse)
+{
+ Vector<int> intVector;
+ intVector.append(10);
+ intVector.append(11);
+ intVector.append(12);
+ intVector.append(13);
+ intVector.reverse();
+
+ EXPECT_EQ(13, intVector[0]);
+ EXPECT_EQ(12, intVector[1]);
+ EXPECT_EQ(11, intVector[2]);
+ EXPECT_EQ(10, intVector[3]);
+
+ intVector.append(9);
+ intVector.reverse();
+
+ EXPECT_EQ(9, intVector[0]);
+ EXPECT_EQ(10, intVector[1]);
+ EXPECT_EQ(11, intVector[2]);
+ EXPECT_EQ(12, intVector[3]);
+ EXPECT_EQ(13, intVector[4]);
+}
+
+TEST(WTF_Vector, ReverseIterator)
+{
+ Vector<int> intVector;
+ intVector.append(10);
+ intVector.append(11);
+ intVector.append(12);
+ intVector.append(13);
+
+ Vector<int>::reverse_iterator it = intVector.rbegin();
+ Vector<int>::reverse_iterator end = intVector.rend();
+ EXPECT_TRUE(end != it);
+
+ EXPECT_EQ(13, *it);
+ ++it;
+ EXPECT_EQ(12, *it);
+ ++it;
+ EXPECT_EQ(11, *it);
+ ++it;
+ EXPECT_EQ(10, *it);
+ ++it;
+
+ EXPECT_TRUE(end == it);
+}
+
+TEST(WTF_Vector, MoveOnly_UncheckedAppend)
+{
+ Vector<MoveOnly> vector;
+
+ vector.reserveInitialCapacity(100);
+ for (size_t i = 0; i < 100; ++i) {
+ MoveOnly moveOnly(i);
+ vector.uncheckedAppend(WTF::move(moveOnly));
+ EXPECT_EQ(0U, moveOnly.value());
+ }
+
+ for (size_t i = 0; i < 100; ++i)
+ EXPECT_EQ(i, vector[i].value());
+}
+
+TEST(WTF_Vector, MoveOnly_Append)
+{
+ Vector<MoveOnly> vector;
+
+ for (size_t i = 0; i < 100; ++i) {
+ MoveOnly moveOnly(i);
+ vector.append(WTF::move(moveOnly));
+ EXPECT_EQ(0U, moveOnly.value());
+ }
+
+ for (size_t i = 0; i < 100; ++i)
+ EXPECT_EQ(i, vector[i].value());
+
+ for (size_t i = 0; i < 16; ++i) {
+ Vector<MoveOnly> vector;
+
+ vector.append(i);
+
+ for (size_t j = 0; j < i; ++j)
+ vector.append(j);
+ vector.append(WTF::move(vector[0]));
+
+ EXPECT_EQ(0U, vector[0].value());
+
+ for (size_t j = 0; j < i; ++j)
+ EXPECT_EQ(j, vector[j + 1].value());
+ EXPECT_EQ(i, vector.last().value());
+ }
+}
+
+TEST(WTF_Vector, MoveOnly_Insert)
+{
+ Vector<MoveOnly> vector;
+
+ for (size_t i = 0; i < 100; ++i) {
+ MoveOnly moveOnly(i);
+ vector.insert(0, WTF::move(moveOnly));
+ EXPECT_EQ(0U, moveOnly.value());
+ }
+
+ EXPECT_EQ(vector.size(), 100U);
+ for (size_t i = 0; i < 100; ++i)
+ EXPECT_EQ(99 - i, vector[i].value());
+
+ for (size_t i = 0; i < 200; i += 2) {
+ MoveOnly moveOnly(1000 + i);
+ vector.insert(i, WTF::move(moveOnly));
+ EXPECT_EQ(0U, moveOnly.value());
+ }
+
+ EXPECT_EQ(200U, vector.size());
+ for (size_t i = 0; i < 200; ++i) {
+ if (i % 2)
+ EXPECT_EQ(99 - i / 2, vector[i].value());
+ else
+ EXPECT_EQ(1000 + i, vector[i].value());
+ }
+}
+
+TEST(WTF_Vector, MoveOnly_TakeLast)
+{
+ Vector<MoveOnly> vector;
+
+ for (size_t i = 0; i < 100; ++i) {
+ MoveOnly moveOnly(i);
+ vector.append(WTF::move(moveOnly));
+ EXPECT_EQ(0U, moveOnly.value());
+ }
+
+ EXPECT_EQ(100U, vector.size());
+ for (size_t i = 0; i < 100; ++i)
+ EXPECT_EQ(99 - i, vector.takeLast().value());
+
+ EXPECT_EQ(0U, vector.size());
+}
+
+TEST(WTF_Vector, VectorOfVectorsOfVectorsInlineCapacitySwap)
+{
+ Vector<Vector<Vector<int, 1>, 1>, 1> a;
+ Vector<Vector<Vector<int, 1>, 1>, 1> b;
+ Vector<Vector<Vector<int, 1>, 1>, 1> c;
+
+ EXPECT_EQ(0U, a.size());
+ EXPECT_EQ(0U, b.size());
+ EXPECT_EQ(0U, c.size());
+
+ Vector<int, 1> x;
+ x.append(42);
+
+ EXPECT_EQ(1U, x.size());
+ EXPECT_EQ(42, x[0]);
+
+ Vector<Vector<int, 1>, 1> y;
+ y.append(x);
+
+ EXPECT_EQ(1U, x.size());
+ EXPECT_EQ(42, x[0]);
+ EXPECT_EQ(1U, y.size());
+ EXPECT_EQ(1U, y[0].size());
+ EXPECT_EQ(42, y[0][0]);
+
+ a.append(y);
+
+ EXPECT_EQ(1U, x.size());
+ EXPECT_EQ(42, x[0]);
+ EXPECT_EQ(1U, y.size());
+ EXPECT_EQ(1U, y[0].size());
+ EXPECT_EQ(42, y[0][0]);
+ EXPECT_EQ(1U, a.size());
+ EXPECT_EQ(1U, a[0].size());
+ EXPECT_EQ(1U, a[0][0].size());
+ EXPECT_EQ(42, a[0][0][0]);
+
+ a.swap(b);
+
+ EXPECT_EQ(0U, a.size());
+ EXPECT_EQ(1U, x.size());
+ EXPECT_EQ(42, x[0]);
+ EXPECT_EQ(1U, y.size());
+ EXPECT_EQ(1U, y[0].size());
+ EXPECT_EQ(42, y[0][0]);
+ EXPECT_EQ(1U, b.size());
+ EXPECT_EQ(1U, b[0].size());
+ EXPECT_EQ(1U, b[0][0].size());
+ EXPECT_EQ(42, b[0][0][0]);
+
+ b.swap(c);
+
+ EXPECT_EQ(0U, a.size());
+ EXPECT_EQ(0U, b.size());
+ EXPECT_EQ(1U, x.size());
+ EXPECT_EQ(42, x[0]);
+ EXPECT_EQ(1U, y.size());
+ EXPECT_EQ(1U, y[0].size());
+ EXPECT_EQ(42, y[0][0]);
+ EXPECT_EQ(1U, c.size());
+ EXPECT_EQ(1U, c[0].size());
+ EXPECT_EQ(1U, c[0][0].size());
+ EXPECT_EQ(42, c[0][0][0]);
+
+ y[0][0] = 24;
+
+ EXPECT_EQ(1U, x.size());
+ EXPECT_EQ(42, x[0]);
+ EXPECT_EQ(1U, y.size());
+ EXPECT_EQ(1U, y[0].size());
+ EXPECT_EQ(24, y[0][0]);
+
+ a.append(y);
+
+ EXPECT_EQ(1U, x.size());
+ EXPECT_EQ(42, x[0]);
+ EXPECT_EQ(1U, y.size());
+ EXPECT_EQ(1U, y[0].size());
+ EXPECT_EQ(24, y[0][0]);
+ EXPECT_EQ(1U, a.size());
+ EXPECT_EQ(1U, a[0].size());
+ EXPECT_EQ(1U, a[0][0].size());
+ EXPECT_EQ(24, a[0][0][0]);
+ EXPECT_EQ(1U, c.size());
+ EXPECT_EQ(1U, c[0].size());
+ EXPECT_EQ(1U, c[0][0].size());
+ EXPECT_EQ(42, c[0][0][0]);
+ EXPECT_EQ(0U, b.size());
+}
+
+TEST(WTF_Vector, RemoveFirst)
+{
+ Vector<int> v;
+ EXPECT_TRUE(v.isEmpty());
+ EXPECT_FALSE(v.removeFirst(1));
+ EXPECT_FALSE(v.removeFirst(-1));
+ EXPECT_TRUE(v.isEmpty());
+
+ v.fill(2, 10);
+ EXPECT_EQ(10U, v.size());
+ EXPECT_FALSE(v.removeFirst(1));
+ EXPECT_EQ(10U, v.size());
+ v.clear();
+
+ v.fill(1, 10);
+ EXPECT_EQ(10U, v.size());
+ EXPECT_TRUE(v.removeFirst(1));
+ EXPECT_TRUE(v == Vector<int>({1, 1, 1, 1, 1, 1, 1, 1, 1}));
+ EXPECT_EQ(9U, v.size());
+ EXPECT_FALSE(v.removeFirst(2));
+ EXPECT_EQ(9U, v.size());
+ EXPECT_TRUE(v == Vector<int>({1, 1, 1, 1, 1, 1, 1, 1, 1}));
+
+ unsigned removed = 0;
+ while (v.removeFirst(1))
+ ++removed;
+ EXPECT_EQ(9U, removed);
+ EXPECT_TRUE(v.isEmpty());
+
+ v.resize(1);
+ EXPECT_EQ(1U, v.size());
+ EXPECT_TRUE(v.removeFirst(1));
+ EXPECT_EQ(0U, v.size());
+ EXPECT_TRUE(v.isEmpty());
+}
+
+TEST(WTF_Vector, RemoveAll)
+{
+ // Using a memcpy-able type.
+ static_assert(VectorTraits<int>::canMoveWithMemcpy, "Should use a memcpy-able type");
+ Vector<int> v;
+ EXPECT_TRUE(v.isEmpty());
+ EXPECT_FALSE(v.removeAll(1));
+ EXPECT_FALSE(v.removeAll(-1));
+ EXPECT_TRUE(v.isEmpty());
+
+ v.fill(1, 10);
+ EXPECT_EQ(10U, v.size());
+ EXPECT_EQ(10U, v.removeAll(1));
+ EXPECT_TRUE(v.isEmpty());
+
+ v.fill(2, 10);
+ EXPECT_EQ(10U, v.size());
+ EXPECT_EQ(0U, v.removeAll(1));
+ EXPECT_EQ(10U, v.size());
+
+ v = {1, 2, 1, 2, 1, 2, 2, 1, 1, 1};
+ EXPECT_EQ(10U, v.size());
+ EXPECT_EQ(6U, v.removeAll(1));
+ EXPECT_EQ(4U, v.size());
+ EXPECT_TRUE(v == Vector<int>({2, 2, 2, 2}));
+ EXPECT_TRUE(v.find(1) == notFound);
+ EXPECT_EQ(4U, v.removeAll(2));
+ EXPECT_TRUE(v.isEmpty());
+
+ v = {3, 1, 2, 1, 2, 1, 2, 2, 1, 1, 1, 3};
+ EXPECT_EQ(12U, v.size());
+ EXPECT_EQ(6U, v.removeAll(1));
+ EXPECT_EQ(6U, v.size());
+ EXPECT_TRUE(v.find(1) == notFound);
+ EXPECT_TRUE(v == Vector<int>({3, 2, 2, 2, 2, 3}));
+
+ EXPECT_EQ(4U, v.removeAll(2));
+ EXPECT_EQ(2U, v.size());
+ EXPECT_TRUE(v.find(2) == notFound);
+ EXPECT_TRUE(v == Vector<int>({3, 3}));
+
+ EXPECT_EQ(2U, v.removeAll(3));
+ EXPECT_TRUE(v.isEmpty());
+
+ v = {1, 1, 1, 3, 2, 4, 2, 2, 2, 4, 4, 3};
+ EXPECT_EQ(12U, v.size());
+ EXPECT_EQ(3U, v.removeAll(1));
+ EXPECT_EQ(9U, v.size());
+ EXPECT_TRUE(v.find(1) == notFound);
+ EXPECT_TRUE(v == Vector<int>({3, 2, 4, 2, 2, 2, 4, 4, 3}));
+
+ // Using a non memcpy-able type.
+ static_assert(!VectorTraits<CString>::canMoveWithMemcpy, "Should use a non memcpy-able type");
+ Vector<CString> vExpected;
+ Vector<CString> v2;
+ EXPECT_TRUE(v2.isEmpty());
+ EXPECT_FALSE(v2.removeAll("1"));
+ EXPECT_TRUE(v2.isEmpty());
+
+ v2.fill("1", 10);
+ EXPECT_EQ(10U, v2.size());
+ EXPECT_EQ(10U, v2.removeAll("1"));
+ EXPECT_TRUE(v2.isEmpty());
+
+ v2.fill("2", 10);
+ EXPECT_EQ(10U, v2.size());
+ EXPECT_EQ(0U, v2.removeAll("1"));
+ EXPECT_EQ(10U, v2.size());
+
+ v2 = {"1", "2", "1", "2", "1", "2", "2", "1", "1", "1"};
+ EXPECT_EQ(10U, v2.size());
+ EXPECT_EQ(6U, v2.removeAll("1"));
+ EXPECT_EQ(4U, v2.size());
+ EXPECT_TRUE(v2.find("1") == notFound);
+ EXPECT_EQ(4U, v2.removeAll("2"));
+ EXPECT_TRUE(v2.isEmpty());
+
+ v2 = {"3", "1", "2", "1", "2", "1", "2", "2", "1", "1", "1", "3"};
+ EXPECT_EQ(12U, v2.size());
+ EXPECT_EQ(6U, v2.removeAll("1"));
+ EXPECT_EQ(6U, v2.size());
+ EXPECT_TRUE(v2.find("1") == notFound);
+ vExpected = {"3", "2", "2", "2", "2", "3"};
+ EXPECT_TRUE(v2 == vExpected);
+
+ EXPECT_EQ(4U, v2.removeAll("2"));
+ EXPECT_EQ(2U, v2.size());
+ EXPECT_TRUE(v2.find("2") == notFound);
+ vExpected = {"3", "3"};
+ EXPECT_TRUE(v2 == vExpected);
+
+ EXPECT_EQ(2U, v2.removeAll("3"));
+ EXPECT_TRUE(v2.isEmpty());
+
+ v2 = {"1", "1", "1", "3", "2", "4", "2", "2", "2", "4", "4", "3"};
+ EXPECT_EQ(12U, v2.size());
+ EXPECT_EQ(3U, v2.removeAll("1"));
+ EXPECT_EQ(9U, v2.size());
+ EXPECT_TRUE(v2.find("1") == notFound);
+ vExpected = {"3", "2", "4", "2", "2", "2", "4", "4", "3"};
+ EXPECT_TRUE(v2 == vExpected);
+}
+
+TEST(WTF_Vector, RemoveFirstMatching)
+{
+ Vector<int> v;
+ EXPECT_TRUE(v.isEmpty());
+ EXPECT_FALSE(v.removeFirstMatching([] (int value) { return value > 0; }));
+ EXPECT_FALSE(v.removeFirstMatching([] (int) { return true; }));
+ EXPECT_FALSE(v.removeFirstMatching([] (int) { return false; }));
+
+ v = {3, 1, 2, 1, 2, 1, 2, 2, 1, 1, 1, 3};
+ EXPECT_EQ(12U, v.size());
+ EXPECT_FALSE(v.removeFirstMatching([] (int) { return false; }));
+ EXPECT_EQ(12U, v.size());
+ EXPECT_FALSE(v.removeFirstMatching([] (int value) { return value < 0; }));
+ EXPECT_EQ(12U, v.size());
+ EXPECT_TRUE(v.removeFirstMatching([] (int value) { return value < 3; }));
+ EXPECT_EQ(11U, v.size());
+ EXPECT_TRUE(v == Vector<int>({3, 2, 1, 2, 1, 2, 2, 1, 1, 1, 3}));
+ EXPECT_TRUE(v.removeFirstMatching([] (int value) { return value > 2; }));
+ EXPECT_EQ(10U, v.size());
+ EXPECT_TRUE(v == Vector<int>({2, 1, 2, 1, 2, 2, 1, 1, 1, 3}));
+ EXPECT_TRUE(v.removeFirstMatching([] (int value) { return value > 2; }));
+ EXPECT_EQ(9U, v.size());
+ EXPECT_TRUE(v == Vector<int>({2, 1, 2, 1, 2, 2, 1, 1, 1}));
+}
+
+TEST(WTF_Vector, RemoveAllMatching)
+{
+ Vector<int> v;
+ EXPECT_TRUE(v.isEmpty());
+ EXPECT_FALSE(v.removeAllMatching([] (int value) { return value > 0; }));
+ EXPECT_FALSE(v.removeAllMatching([] (int) { return true; }));
+ EXPECT_FALSE(v.removeAllMatching([] (int) { return false; }));
+
+ v = {3, 1, 2, 1, 2, 1, 2, 2, 1, 1, 1, 3};
+ EXPECT_EQ(12U, v.size());
+ EXPECT_EQ(0U, v.removeAllMatching([] (int) { return false; }));
+ EXPECT_EQ(12U, v.size());
+ EXPECT_EQ(0U, v.removeAllMatching([] (int value) { return value < 0; }));
+ EXPECT_EQ(12U, v.size());
+ EXPECT_EQ(12U, v.removeAllMatching([] (int value) { return value > 0; }));
+ EXPECT_TRUE(v.isEmpty());
+
+ v = {3, 1, 2, 1, 2, 1, 3, 2, 2, 1, 1, 1, 3};
+ EXPECT_EQ(13U, v.size());
+ EXPECT_EQ(3U, v.removeAllMatching([] (int value) { return value > 2; }));
+ EXPECT_EQ(10U, v.size());
+ EXPECT_TRUE(v == Vector<int>({1, 2, 1, 2, 1, 2, 2, 1, 1, 1}));
+ EXPECT_EQ(6U, v.removeAllMatching([] (int value) { return value != 2; }));
+ EXPECT_EQ(4U, v.size());
+ EXPECT_TRUE(v == Vector<int>({2, 2, 2, 2}));
+ EXPECT_EQ(4U, v.removeAllMatching([] (int value) { return value == 2; }));
+ EXPECT_TRUE(v.isEmpty());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp b/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp
new file mode 100644
index 000000000..a13ca0aa0
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <limits>
+#include <wtf/MathExtras.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF, StringCreationFromLiteral)
+{
+ String stringFromLiteral(ASCIILiteral("Explicit construction syntax"));
+ ASSERT_EQ(strlen("Explicit construction syntax"), stringFromLiteral.length());
+ ASSERT_TRUE(stringFromLiteral == "Explicit construction syntax");
+ ASSERT_TRUE(stringFromLiteral.is8Bit());
+ ASSERT_TRUE(String("Explicit construction syntax") == stringFromLiteral);
+
+ String stringWithTemplate("Template Literal", String::ConstructFromLiteral);
+ ASSERT_EQ(strlen("Template Literal"), stringWithTemplate.length());
+ ASSERT_TRUE(stringWithTemplate == "Template Literal");
+ ASSERT_TRUE(stringWithTemplate.is8Bit());
+ ASSERT_TRUE(String("Template Literal") == stringWithTemplate);
+}
+
+TEST(WTF, StringASCII)
+{
+ CString output;
+
+ // Null String.
+ output = String().ascii();
+ ASSERT_STREQ("", output.data());
+
+ // Empty String.
+ output = emptyString().ascii();
+ ASSERT_STREQ("", output.data());
+
+ // Regular String.
+ output = String(ASCIILiteral("foobar")).ascii();
+ ASSERT_STREQ("foobar", output.data());
+}
+
+static void testNumberToStringECMAScript(double number, const char* reference)
+{
+ CString numberString = String::numberToStringECMAScript(number).latin1();
+ ASSERT_STREQ(reference, numberString.data());
+}
+
+TEST(WTF, StringNumberToStringECMAScriptBoundaries)
+{
+ typedef std::numeric_limits<double> Limits;
+
+ // Infinity.
+ testNumberToStringECMAScript(Limits::infinity(), "Infinity");
+ testNumberToStringECMAScript(-Limits::infinity(), "-Infinity");
+
+ // NaN.
+ testNumberToStringECMAScript(-Limits::quiet_NaN(), "NaN");
+
+ // Zeros.
+ testNumberToStringECMAScript(0, "0");
+ testNumberToStringECMAScript(-0, "0");
+
+ // Min-Max.
+ testNumberToStringECMAScript(Limits::min(), "2.2250738585072014e-308");
+ testNumberToStringECMAScript(Limits::max(), "1.7976931348623157e+308");
+}
+
+TEST(WTF, StringNumberToStringECMAScriptRegularNumbers)
+{
+ // Pi.
+ testNumberToStringECMAScript(piDouble, "3.141592653589793");
+ testNumberToStringECMAScript(piFloat, "3.1415927410125732");
+ testNumberToStringECMAScript(piOverTwoDouble, "1.5707963267948966");
+ testNumberToStringECMAScript(piOverTwoFloat, "1.5707963705062866");
+ testNumberToStringECMAScript(piOverFourDouble, "0.7853981633974483");
+ testNumberToStringECMAScript(piOverFourFloat, "0.7853981852531433");
+
+ // e.
+ const double e = 2.71828182845904523536028747135266249775724709369995;
+ testNumberToStringECMAScript(e, "2.718281828459045");
+
+ // c, speed of light in m/s.
+ const double c = 299792458;
+ testNumberToStringECMAScript(c, "299792458");
+
+ // Golen ratio.
+ const double phi = 1.6180339887498948482;
+ testNumberToStringECMAScript(phi, "1.618033988749895");
+}
+
+TEST(WTF, StringReplaceWithLiteral)
+{
+ // Cases for 8Bit source.
+ String testString = "1224";
+ ASSERT_TRUE(testString.is8Bit());
+ testString.replaceWithLiteral('2', "");
+ ASSERT_STREQ("14", testString.utf8().data());
+
+ testString = "1224";
+ ASSERT_TRUE(testString.is8Bit());
+ testString.replaceWithLiteral('2', "3");
+ ASSERT_STREQ("1334", testString.utf8().data());
+
+ testString = "1224";
+ ASSERT_TRUE(testString.is8Bit());
+ testString.replaceWithLiteral('2', "555");
+ ASSERT_STREQ("15555554", testString.utf8().data());
+
+ testString = "1224";
+ ASSERT_TRUE(testString.is8Bit());
+ testString.replaceWithLiteral('3', "NotFound");
+ ASSERT_STREQ("1224", testString.utf8().data());
+
+ // Cases for 16Bit source.
+ testString = String::fromUTF8("résumé");
+ ASSERT_FALSE(testString.is8Bit());
+ testString.replaceWithLiteral(UChar(0x00E9 /*U+00E9 is 'é'*/), "e");
+ ASSERT_STREQ("resume", testString.utf8().data());
+
+ testString = String::fromUTF8("résumé");
+ ASSERT_FALSE(testString.is8Bit());
+ testString.replaceWithLiteral(UChar(0x00E9 /*U+00E9 is 'é'*/), "");
+ ASSERT_STREQ("rsum", testString.utf8().data());
+
+ testString = String::fromUTF8("résumé");
+ ASSERT_FALSE(testString.is8Bit());
+ testString.replaceWithLiteral('3', "NotFound");
+ ASSERT_STREQ("résumé", testString.utf8().data());
+}
+
+TEST(WTF, StringIsolatedCopy)
+{
+ String original = "1234";
+ auto copy = WTF::move(original).isolatedCopy();
+ ASSERT_FALSE(original.impl() == copy.impl());
+}
+
+TEST(WTF, StringToInt)
+{
+ bool ok;
+
+ EXPECT_EQ(0, String().toInt());
+ EXPECT_EQ(0, String().toInt(&ok));
+ EXPECT_FALSE(ok);
+
+ EXPECT_EQ(0, emptyString().toInt());
+ EXPECT_EQ(0, emptyString().toInt(&ok));
+ EXPECT_FALSE(ok);
+
+ EXPECT_EQ(0, String("0").toInt());
+ EXPECT_EQ(0, String("0").toInt(&ok));
+ EXPECT_TRUE(ok);
+
+ EXPECT_EQ(1, String("1").toInt());
+ EXPECT_EQ(1, String("1").toInt(&ok));
+ EXPECT_TRUE(ok);
+
+ EXPECT_EQ(2147483647, String("2147483647").toInt());
+ EXPECT_EQ(2147483647, String("2147483647").toInt(&ok));
+ EXPECT_TRUE(ok);
+
+ EXPECT_EQ(0, String("2147483648").toInt());
+ EXPECT_EQ(0, String("2147483648").toInt(&ok));
+ EXPECT_FALSE(ok);
+
+ EXPECT_EQ(-2147483648, String("-2147483648").toInt());
+ EXPECT_EQ(-2147483648, String("-2147483648").toInt(&ok));
+ EXPECT_TRUE(ok);
+
+ EXPECT_EQ(0, String("-2147483649").toInt());
+ EXPECT_EQ(0, String("-2147483649").toInt(&ok));
+ EXPECT_FALSE(ok);
+
+ // fail if we see leading junk
+ EXPECT_EQ(0, String("x1").toInt());
+ EXPECT_EQ(0, String("x1").toInt(&ok));
+ EXPECT_FALSE(ok);
+
+ // succeed if we see leading spaces
+ EXPECT_EQ(1, String(" 1").toInt());
+ EXPECT_EQ(1, String(" 1").toInt(&ok));
+ EXPECT_TRUE(ok);
+
+ // silently ignore trailing junk
+ EXPECT_EQ(1, String("1x").toInt());
+ EXPECT_EQ(1, String("1x").toInt(&ok));
+ EXPECT_TRUE(ok);
+}
+
+TEST(WTF, StringToDouble)
+{
+ bool ok;
+
+ EXPECT_EQ(0.0, String().toDouble());
+ EXPECT_EQ(0.0, String().toDouble(&ok));
+ EXPECT_FALSE(ok);
+
+ EXPECT_EQ(0.0, emptyString().toDouble());
+ EXPECT_EQ(0.0, emptyString().toDouble(&ok));
+ EXPECT_FALSE(ok);
+
+ EXPECT_EQ(0.0, String("0").toDouble());
+ EXPECT_EQ(0.0, String("0").toDouble(&ok));
+ EXPECT_TRUE(ok);
+
+ EXPECT_EQ(1.0, String("1").toDouble());
+ EXPECT_EQ(1.0, String("1").toDouble(&ok));
+ EXPECT_TRUE(ok);
+
+ // fail if we see leading junk
+ EXPECT_EQ(0.0, String("x1").toDouble());
+ EXPECT_EQ(0.0, String("x1").toDouble(&ok));
+ EXPECT_FALSE(ok);
+
+ // succeed if we see leading spaces
+ EXPECT_EQ(1.0, String(" 1").toDouble());
+ EXPECT_EQ(1.0, String(" 1").toDouble(&ok));
+ EXPECT_TRUE(ok);
+
+ // ignore trailing junk, but return false for "ok"
+ // FIXME: This is an inconsistency with toInt, which always guarantees
+ // it will return 0 if it's also going to return false for ok.
+ EXPECT_EQ(1.0, String("1x").toDouble());
+ EXPECT_EQ(1.0, String("1x").toDouble(&ok));
+ EXPECT_FALSE(ok);
+
+ // parse only numbers, not special values such as "infinity"
+ EXPECT_EQ(0.0, String("infinity").toDouble());
+ EXPECT_EQ(0.0, String("infinity").toDouble(&ok));
+ EXPECT_FALSE(ok);
+
+ // parse only numbers, not special values such as "nan"
+ EXPECT_EQ(0.0, String("nan").toDouble());
+ EXPECT_EQ(0.0, String("nan").toDouble(&ok));
+ EXPECT_FALSE(ok);
+}
+
+TEST(WTF, StringhasInfixStartingAt)
+{
+ EXPECT_TRUE(String("Test").is8Bit());
+ EXPECT_TRUE(String("Te").is8Bit());
+ EXPECT_TRUE(String("st").is8Bit());
+ EXPECT_TRUE(String("Test").hasInfixStartingAt(String("Te"), 0));
+ EXPECT_FALSE(String("Test").hasInfixStartingAt(String("Te"), 2));
+ EXPECT_TRUE(String("Test").hasInfixStartingAt(String("st"), 2));
+ EXPECT_FALSE(String("Test").hasInfixStartingAt(String("ST"), 2));
+
+ EXPECT_FALSE(String::fromUTF8("中国").is8Bit());
+ EXPECT_FALSE(String::fromUTF8("中").is8Bit());
+ EXPECT_FALSE(String::fromUTF8("国").is8Bit());
+ EXPECT_TRUE(String::fromUTF8("中国").hasInfixStartingAt(String::fromUTF8("中"), 0));
+ EXPECT_FALSE(String::fromUTF8("中国").hasInfixStartingAt(String::fromUTF8("中"), 1));
+ EXPECT_TRUE(String::fromUTF8("中国").hasInfixStartingAt(String::fromUTF8("国"), 1));
+
+ EXPECT_FALSE(String::fromUTF8("中国").hasInfixStartingAt(String("Te"), 0));
+ EXPECT_FALSE(String("Test").hasInfixStartingAt(String::fromUTF8("中"), 2));
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp
new file mode 100644
index 000000000..5b900007a
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "test.h"
+
+#include <wtf/WeakPtr.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF_WeakPtr, Basic)
+{
+ int dummy = 5;
+ WeakPtrFactory<int>* factory = new WeakPtrFactory<int>(&dummy);
+ WeakPtr<int> weakPtr1 = factory->createWeakPtr();
+ WeakPtr<int> weakPtr2 = factory->createWeakPtr();
+ WeakPtr<int> weakPtr3 = factory->createWeakPtr();
+ EXPECT_EQ(weakPtr1.get(), &dummy);
+ EXPECT_EQ(weakPtr2.get(), &dummy);
+ EXPECT_EQ(weakPtr3.get(), &dummy);
+ EXPECT_TRUE(weakPtr1);
+ EXPECT_TRUE(weakPtr2);
+ EXPECT_TRUE(weakPtr3);
+ delete factory;
+ EXPECT_NULL(weakPtr1.get());
+ EXPECT_NULL(weakPtr2.get());
+ EXPECT_NULL(weakPtr3.get());
+ EXPECT_FALSE(weakPtr1);
+ EXPECT_FALSE(weakPtr2);
+ EXPECT_FALSE(weakPtr3);
+}
+
+TEST(WTF_WeakPtr, Assignment)
+{
+ int dummy = 5;
+ WeakPtr<int> weakPtr;
+ {
+ WeakPtrFactory<int> factory(&dummy);
+ EXPECT_NULL(weakPtr.get());
+ weakPtr = factory.createWeakPtr();
+ EXPECT_EQ(weakPtr.get(), &dummy);
+ }
+ EXPECT_NULL(weakPtr.get());
+}
+
+TEST(WTF_WeakPtr, MultipleFactories)
+{
+ int dummy1 = 5;
+ int dummy2 = 7;
+ WeakPtrFactory<int>* factory1 = new WeakPtrFactory<int>(&dummy1);
+ WeakPtrFactory<int>* factory2 = new WeakPtrFactory<int>(&dummy2);
+ WeakPtr<int> weakPtr1 = factory1->createWeakPtr();
+ WeakPtr<int> weakPtr2 = factory2->createWeakPtr();
+ EXPECT_EQ(weakPtr1.get(), &dummy1);
+ EXPECT_EQ(weakPtr2.get(), &dummy2);
+ delete factory1;
+ EXPECT_NULL(weakPtr1.get());
+ EXPECT_EQ(weakPtr2.get(), &dummy2);
+ delete factory2;
+ EXPECT_NULL(weakPtr2.get());
+}
+
+TEST(WTF_WeakPtr, RevokeAll)
+{
+ int dummy = 5;
+ WeakPtrFactory<int> factory(&dummy);
+ WeakPtr<int> weakPtr1 = factory.createWeakPtr();
+ WeakPtr<int> weakPtr2 = factory.createWeakPtr();
+ WeakPtr<int> weakPtr3 = factory.createWeakPtr();
+ EXPECT_EQ(weakPtr1.get(), &dummy);
+ EXPECT_EQ(weakPtr2.get(), &dummy);
+ EXPECT_EQ(weakPtr3.get(), &dummy);
+ factory.revokeAll();
+ EXPECT_NULL(weakPtr1.get());
+ EXPECT_NULL(weakPtr2.get());
+ EXPECT_NULL(weakPtr3.get());
+}
+
+TEST(WTF_WeakPtr, NullFactory)
+{
+ WeakPtrFactory<int> factory(nullptr);
+ WeakPtr<int> weakPtr = factory.createWeakPtr();
+ EXPECT_NULL(weakPtr.get());
+ factory.revokeAll();
+ EXPECT_NULL(weakPtr.get());
+}
+
+struct Foo {
+ void bar() { };
+};
+
+TEST(WTF_WeakPtr, Dereference)
+{
+ Foo f;
+ WeakPtrFactory<Foo> factory(&f);
+ WeakPtr<Foo> weakPtr = factory.createWeakPtr();
+ weakPtr->bar();
+}
+
+TEST(WTF_WeakPtr, Forget)
+{
+ int dummy = 5;
+ int dummy2 = 7;
+
+ WeakPtrFactory<int> outerFactory(&dummy2);
+ WeakPtr<int> weakPtr1, weakPtr2, weakPtr3, weakPtr4;
+ {
+ WeakPtrFactory<int> innerFactory(&dummy);
+ weakPtr1 = innerFactory.createWeakPtr();
+ weakPtr2 = innerFactory.createWeakPtr();
+ weakPtr3 = innerFactory.createWeakPtr();
+ EXPECT_EQ(weakPtr1.get(), &dummy);
+ EXPECT_EQ(weakPtr2.get(), &dummy);
+ EXPECT_EQ(weakPtr3.get(), &dummy);
+ weakPtr1.clear();
+ weakPtr3 = nullptr;
+ EXPECT_NULL(weakPtr1.get());
+ EXPECT_EQ(weakPtr2.get(), &dummy);
+ EXPECT_NULL(weakPtr3.get());
+ weakPtr1.clear();
+ weakPtr3.clear();
+ EXPECT_NULL(weakPtr1.get());
+ EXPECT_EQ(weakPtr2.get(), &dummy);
+ EXPECT_NULL(weakPtr3.get());
+ weakPtr3 = nullptr;
+ EXPECT_NULL(weakPtr1.get());
+ EXPECT_EQ(weakPtr2.get(), &dummy);
+ EXPECT_NULL(weakPtr3.get());
+
+ weakPtr4 = weakPtr2;
+ EXPECT_EQ(weakPtr2.get(), &dummy);
+ EXPECT_EQ(weakPtr4.get(), &dummy);
+
+ WeakPtr<int> weakPtr5 = weakPtr2;
+ EXPECT_EQ(weakPtr2.get(), &dummy);
+ EXPECT_EQ(weakPtr5.get(), &dummy);
+ weakPtr5.clear();
+ EXPECT_NULL(weakPtr5.get());
+ EXPECT_EQ(weakPtr2.get(), &dummy);
+
+ weakPtr4 = outerFactory.createWeakPtr();
+ EXPECT_EQ(weakPtr2.get(), &dummy);
+ EXPECT_EQ(weakPtr4.get(), &dummy2);
+ }
+
+ EXPECT_NULL(weakPtr1.get());
+ EXPECT_NULL(weakPtr2.get());
+ EXPECT_EQ(weakPtr4.get(), &dummy2);
+
+ WeakPtr<int> weakPtr5 = weakPtr4;
+ EXPECT_EQ(weakPtr4.get(), &dummy2);
+ EXPECT_EQ(weakPtr5.get(), &dummy2);
+ weakPtr5.clear();
+ EXPECT_NULL(weakPtr5.get());
+ WeakPtr<int> weakPtr6 = weakPtr5;
+ EXPECT_NULL(weakPtr6.get());
+ EXPECT_EQ(weakPtr5.get(), weakPtr6.get());
+
+ WeakPtr<int> weakPtr7 = outerFactory.createWeakPtr();
+ EXPECT_EQ(weakPtr7.get(), &dummy2);
+ weakPtr7 = nullptr;
+ EXPECT_NULL(weakPtr7.get());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/WorkQueue.cpp b/Tools/TestWebKitAPI/Tests/WTF/WorkQueue.cpp
new file mode 100644
index 000000000..74f5dc294
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/WorkQueue.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "Test.h"
+#include <wtf/Condition.h>
+#include <wtf/Lock.h>
+#include <wtf/Vector.h>
+#include <wtf/WorkQueue.h>
+#include <string>
+#include <thread>
+
+namespace TestWebKitAPI {
+
+static const char* simpleTestLabel = "simpleTest";
+static const char* longTestLabel = "longTest";
+static const char* thirdTestLabel = "thirdTest";
+static const char* dispatchAfterLabel = "dispatchAfter";
+
+TEST(WTF_WorkQueue, Simple)
+{
+ Lock m_lock;
+ Condition m_testCompleted;
+ Vector<std::string> m_functionCallOrder;
+
+ bool calledSimpleTest = false;
+ bool calledLongTest = false;
+ bool calledThirdTest = false;
+
+ static const char* simpleTestLabel = "simpleTest";
+ static const char* longTestLabel = "longTest";
+ static const char* thirdTestLabel = "thirdTest";
+
+ auto queue = WorkQueue::create("com.apple.WebKit.Test.simple");
+ int initialRefCount = queue->refCount();
+ EXPECT_EQ(1, initialRefCount);
+
+ LockHolder locker(m_lock);
+ queue->dispatch([&](void) {
+ m_functionCallOrder.append(simpleTestLabel);
+ calledSimpleTest = true;
+ });
+
+ queue->dispatch([&](void) {
+ m_functionCallOrder.append(longTestLabel);
+ std::this_thread::sleep_for(std::chrono::nanoseconds(100));
+ calledLongTest = true;
+ });
+
+ queue->dispatch([&](void) {
+ LockHolder locker(m_lock);
+ m_functionCallOrder.append(thirdTestLabel);
+ calledThirdTest = true;
+
+ EXPECT_TRUE(calledSimpleTest);
+ EXPECT_TRUE(calledLongTest);
+ EXPECT_TRUE(calledThirdTest);
+
+ m_testCompleted.notifyOne();
+ });
+
+ EXPECT_GT(queue->refCount(), 1);
+
+ m_testCompleted.wait(m_lock);
+
+ EXPECT_TRUE(calledSimpleTest);
+ EXPECT_TRUE(calledLongTest);
+ EXPECT_TRUE(calledThirdTest);
+
+ EXPECT_EQ(static_cast<size_t>(3), m_functionCallOrder.size());
+ EXPECT_STREQ(simpleTestLabel, m_functionCallOrder[0].c_str());
+ EXPECT_STREQ(longTestLabel, m_functionCallOrder[1].c_str());
+ EXPECT_STREQ(thirdTestLabel, m_functionCallOrder[2].c_str());
+}
+
+TEST(WTF_WorkQueue, TwoQueues)
+{
+ Lock m_lock;
+ Condition m_testQueue1Completed, m_testQueue2Completed;
+ Vector<std::string> m_functionCallOrder;
+
+ bool calledSimpleTest = false;
+ bool calledLongTest = false;
+ bool calledThirdTest = false;
+
+ auto queue1 = WorkQueue::create("com.apple.WebKit.Test.twoQueues1");
+ auto queue2 = WorkQueue::create("com.apple.WebKit.Test.twoQueues2");
+
+ EXPECT_EQ(1, queue1->refCount());
+ EXPECT_EQ(1, queue2->refCount());
+
+ LockHolder locker(m_lock);
+
+ queue1->dispatch([&](void) {
+ m_functionCallOrder.append(simpleTestLabel);
+ calledSimpleTest = true;
+ });
+
+ queue2->dispatch([&](void) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+ LockHolder locker(m_lock);
+
+ // Will fail if queue2 took the mutex before queue1.
+ EXPECT_TRUE(calledThirdTest);
+
+ m_functionCallOrder.append(longTestLabel);
+ calledLongTest = true;
+ m_testQueue2Completed.notifyOne();
+ });
+
+ queue1->dispatch([&](void) {
+ LockHolder locker(m_lock);
+ m_functionCallOrder.append(thirdTestLabel);
+ calledThirdTest = true;
+
+ m_testQueue1Completed.notifyOne();
+ });
+
+ m_testQueue1Completed.wait(m_lock);
+
+ EXPECT_TRUE(calledSimpleTest);
+ EXPECT_FALSE(calledLongTest);
+ EXPECT_TRUE(calledThirdTest);
+
+ m_testQueue2Completed.wait(m_lock);
+
+ EXPECT_TRUE(calledSimpleTest);
+ EXPECT_TRUE(calledLongTest);
+ EXPECT_TRUE(calledThirdTest);
+
+ EXPECT_EQ(static_cast<size_t>(3), m_functionCallOrder.size());
+ EXPECT_STREQ(simpleTestLabel, m_functionCallOrder[0].c_str());
+ EXPECT_STREQ(thirdTestLabel, m_functionCallOrder[1].c_str());
+ EXPECT_STREQ(longTestLabel, m_functionCallOrder[2].c_str());
+}
+
+TEST(WTF_WorkQueue, DispatchAfter)
+{
+ Lock m_lock;
+ Condition m_testCompleted, m_dispatchAfterTestCompleted;
+ Vector<std::string> m_functionCallOrder;
+
+ bool calledSimpleTest = false;
+ bool calledDispatchAfterTest = false;
+
+ auto queue = WorkQueue::create("com.apple.WebKit.Test.dispatchAfter");
+
+ LockHolder locker(m_lock);
+
+ queue->dispatch([&](void) {
+ LockHolder locker(m_lock);
+ m_functionCallOrder.append(simpleTestLabel);
+ calledSimpleTest = true;
+ m_testCompleted.notifyOne();
+ });
+
+ queue->dispatchAfter(std::chrono::milliseconds(500), [&](void) {
+ LockHolder locker(m_lock);
+ m_functionCallOrder.append(dispatchAfterLabel);
+ calledDispatchAfterTest = true;
+ m_dispatchAfterTestCompleted.notifyOne();
+ });
+
+ m_testCompleted.wait(m_lock);
+
+ EXPECT_TRUE(calledSimpleTest);
+ EXPECT_FALSE(calledDispatchAfterTest);
+
+ m_dispatchAfterTestCompleted.wait(m_lock);
+
+ EXPECT_TRUE(calledSimpleTest);
+ EXPECT_TRUE(calledDispatchAfterTest);
+
+ EXPECT_EQ(static_cast<size_t>(2), m_functionCallOrder.size());
+ EXPECT_STREQ(simpleTestLabel, m_functionCallOrder[0].c_str());
+ EXPECT_STREQ(dispatchAfterLabel, m_functionCallOrder[1].c_str());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/darwin/OSObjectPtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/darwin/OSObjectPtr.cpp
new file mode 100644
index 000000000..5389dcbc8
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/darwin/OSObjectPtr.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/OSObjectPtr.h>
+
+#include <dispatch/dispatch.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+namespace TestWebKitAPI {
+
+TEST(OSObjectPtr, AdoptOSObject)
+{
+ OSObjectPtr<dispatch_queue_t> foo = adoptOSObject(dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL));
+
+ EXPECT_EQ(1, CFGetRetainCount(foo.get()));
+}
+
+TEST(OSObjectPtr, RetainRelease)
+{
+ dispatch_queue_t foo = dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL);
+ EXPECT_EQ(1, CFGetRetainCount(foo));
+
+ WTF::retainOSObject(foo);
+ EXPECT_EQ(2, CFGetRetainCount(foo));
+
+ WTF::releaseOSObject(foo);
+ EXPECT_EQ(1, CFGetRetainCount(foo));
+}
+
+TEST(OSObjectPtr, LeakRef)
+{
+ OSObjectPtr<dispatch_queue_t> foo = adoptOSObject(dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL));
+ EXPECT_EQ(1, CFGetRetainCount(foo.get()));
+
+ dispatch_queue_t queue = foo.leakRef();
+ EXPECT_EQ(nullptr, foo.get());
+ EXPECT_EQ(1, CFGetRetainCount(queue));
+
+ WTF::releaseOSObject(queue);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/glib/GMainLoopSource.cpp b/Tools/TestWebKitAPI/Tests/WTF/glib/GMainLoopSource.cpp
new file mode 100644
index 000000000..54991eeeb
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/glib/GMainLoopSource.cpp
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <wtf/glib/GThreadSafeMainLoopSource.h>
+#include <stdio.h>
+
+namespace TestWebKitAPI {
+
+template <typename T>
+class GMainLoopSourceTest {
+public:
+ GMainLoopSourceTest()
+ : m_mainLoop(g_main_loop_new(nullptr, TRUE))
+ {
+ }
+
+ ~GMainLoopSourceTest()
+ {
+ g_main_loop_unref(m_mainLoop);
+ }
+
+ void runLoop()
+ {
+ g_main_loop_run(m_mainLoop);
+ }
+
+ void delayedFinish()
+ {
+ g_timeout_add(250,
+ [](gpointer data) {
+ GMainLoopSourceTest& test = *static_cast<GMainLoopSourceTest*>(data);
+ test.finish();
+ return G_SOURCE_REMOVE;
+ }, this);
+ }
+
+ void finish()
+ {
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ T& source() { return m_source; }
+
+private:
+ GMainLoop* m_mainLoop;
+ T m_source;
+};
+
+template <typename T>
+static void basicRescheduling(T& context)
+{
+ EXPECT_TRUE(!context.test.source().isActive());
+
+ context.test.source().schedule("[Test] FirstTask", [&] {
+ // This should never be called. That's why we assert
+ // that the variable is false a few lines later.
+ context.finishedFirstTask = true;
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.test.source().schedule("[Test] SecondTask", [&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ context.finishedSecondTask = true;
+ context.test.finish();
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.test.runLoop();
+
+ EXPECT_TRUE(!context.test.source().isActive());
+ EXPECT_FALSE(context.finishedFirstTask);
+ EXPECT_TRUE(context.finishedSecondTask);
+}
+
+TEST(WTF_GMainLoopSource, BasicRescheduling)
+{
+ struct TestingContext {
+ GMainLoopSourceTest<GMainLoopSource> test;
+ bool finishedFirstTask = false;
+ bool finishedSecondTask = false;
+ } context;
+ basicRescheduling<TestingContext>(context);
+
+ struct ThreadSafeTestingContext {
+ GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
+ bool finishedFirstTask = false;
+ bool finishedSecondTask = false;
+ } threadSafeContext;
+ basicRescheduling<ThreadSafeTestingContext>(threadSafeContext);
+}
+
+template <typename T>
+static void reentrantRescheduling(T& context)
+{
+ EXPECT_TRUE(!context.test.source().isActive());
+
+ context.test.source().schedule("[Test] FirstTask", [&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+
+ context.test.source().schedule("[Test] SecondTask", [&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ EXPECT_TRUE(context.finishedFirstTask);
+
+ context.finishedSecondTask = true;
+ context.test.finish();
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.finishedFirstTask = true;
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.test.runLoop();
+
+ EXPECT_TRUE(!context.test.source().isActive());
+ EXPECT_TRUE(context.finishedFirstTask);
+ EXPECT_TRUE(context.finishedSecondTask);
+}
+
+TEST(WTF_GMainLoopSource, ReentrantRescheduling)
+{
+ struct TestingContext {
+ GMainLoopSourceTest<GMainLoopSource> test;
+ bool finishedFirstTask = false;
+ bool finishedSecondTask = false;
+ } context;
+ reentrantRescheduling<TestingContext>(context);
+
+ struct ThreadSafeTestingContext {
+ GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
+ bool finishedFirstTask = false;
+ bool finishedSecondTask = false;
+ } threadSafeContext;
+ reentrantRescheduling<ThreadSafeTestingContext>(threadSafeContext);
+}
+
+TEST(WTF_GMainLoopSource, ReschedulingFromDifferentThread)
+{
+ struct TestingContext {
+ GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
+ bool finishedFirstTask;
+ bool finishedSecondTask;
+ } context;
+
+ EXPECT_TRUE(!context.test.source().isActive());
+
+ context.test.source().schedule("[Test] FirstTask", [&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+
+ g_usleep(1 * G_USEC_PER_SEC);
+ context.finishedFirstTask = true;
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ GThread* helperThread = g_thread_new(nullptr, [](gpointer data) -> gpointer {
+ g_usleep(0.25 * G_USEC_PER_SEC);
+
+ TestingContext& context = *static_cast<TestingContext*>(data);
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ EXPECT_FALSE(context.finishedFirstTask);
+
+ context.test.source().schedule("[Test] SecondTask", [&] {
+ EXPECT_TRUE(context.finishedFirstTask);
+
+ context.finishedSecondTask = true;
+ context.test.finish();
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ g_thread_exit(nullptr);
+ return nullptr;
+ }, &context);
+
+ context.test.runLoop();
+ g_thread_unref(helperThread);
+
+ EXPECT_TRUE(!context.test.source().isActive());
+ EXPECT_TRUE(context.finishedFirstTask);
+ EXPECT_TRUE(context.finishedSecondTask);
+}
+
+TEST(WTF_GMainLoopSource, DestructionDuringDispatch)
+{
+ // This is just a raw test that ensures deleting the GMainLoopSource object during
+ // dispatch does not cause problems. This test succeeds if it doesn't crash.
+
+ GMainLoopSource* source;
+ GMainLoop* loop = g_main_loop_new(nullptr, TRUE);
+
+ source = new GMainLoopSource;
+ source->schedule("[Test] DestroySourceTask", [&] {
+ delete source;
+ g_main_loop_quit(loop);
+ });
+ g_main_loop_run(loop);
+
+ source = new GMainLoopSource;
+ source->schedule("[Test] DestroySourceTask", std::function<bool ()>([&] {
+ delete source;
+ g_main_loop_quit(loop);
+ return false;
+ }));
+ g_main_loop_run(loop);
+
+ g_main_loop_unref(loop);
+}
+
+template <typename T>
+static void cancelRepeatingSourceDuringDispatch(T& context)
+{
+ EXPECT_TRUE(!context.test.source().isActive());
+
+ context.test.source().schedule("[Test] RepeatingTask",
+ std::function<bool ()>([&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+
+ context.callCount++;
+ if (context.callCount == 3)
+ context.test.source().cancel();
+ return true;
+ }));
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.test.delayedFinish();
+ context.test.runLoop();
+
+ EXPECT_TRUE(!context.test.source().isActive());
+ EXPECT_EQ(3, context.callCount);
+}
+
+TEST(WTF_GMainLoopSource, CancelRepeatingSourceDuringDispatch)
+{
+ struct TestingContext {
+ GMainLoopSourceTest<GMainLoopSource> test;
+ unsigned callCount = 0;
+ } context;
+ cancelRepeatingSourceDuringDispatch<TestingContext>(context);
+
+ struct ThreadSafeTestingContext {
+ GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
+ unsigned callCount = 0;
+ } threadSafeContext;
+ cancelRepeatingSourceDuringDispatch<ThreadSafeTestingContext>(threadSafeContext);
+}
+
+template <typename T>
+static void basicDestroyCallbacks()
+{
+ {
+ T context;
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.test.source().schedule("[Test] DestroyCallback",
+ [&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ context.callbackCalled = true;
+ }, G_PRIORITY_DEFAULT,
+ [&] {
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.destroyCallbackCalled = true;
+ context.test.finish();
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.test.runLoop();
+
+ EXPECT_TRUE(!context.test.source().isActive());
+ EXPECT_TRUE(context.callbackCalled);
+ EXPECT_TRUE(context.destroyCallbackCalled);
+ }
+
+ {
+ T context;
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.test.source().schedule("[Test] DestroyCallback",
+ std::function<bool ()>([&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ context.callbackCalled = true;
+ return false;
+ }), G_PRIORITY_DEFAULT,
+ [&] {
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.destroyCallbackCalled = true;
+ context.test.finish();
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.test.runLoop();
+
+ EXPECT_TRUE(!context.test.source().isActive());
+ EXPECT_TRUE(context.callbackCalled);
+ EXPECT_TRUE(context.destroyCallbackCalled);
+ }
+}
+
+TEST(WTF_GMainLoopSource, BasicDestroyCallbacks)
+{
+ struct TestingContext {
+ GMainLoopSourceTest<GMainLoopSource> test;
+ bool callbackCalled = false;
+ bool destroyCallbackCalled = false;
+ };
+ basicDestroyCallbacks<TestingContext>();
+
+ struct ThreadSafeTestingContext {
+ GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
+ bool callbackCalled = false;
+ bool destroyCallbackCalled = false;
+ };
+ basicDestroyCallbacks<ThreadSafeTestingContext>();
+}
+
+template <typename T>
+static void destroyCallbacksAfterCancellingDuringDispatch()
+{
+ {
+ T context;
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.test.source().schedule("[Test] DestroyCallback",
+ [&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ context.callbackCallCount++;
+ context.test.source().cancel();
+ }, G_PRIORITY_DEFAULT,
+ [&] {
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.destroyCallbackCalled = true;
+ context.test.finish();
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.test.delayedFinish();
+ context.test.runLoop();
+
+ EXPECT_TRUE(!context.test.source().isActive());
+ EXPECT_EQ(1, context.callbackCallCount);
+ EXPECT_TRUE(context.destroyCallbackCalled);
+ }
+
+ {
+ T context;
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.test.source().schedule("[Test] DestroyCallback",
+ std::function<bool ()>([&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ context.callbackCallCount++;
+ if (context.callbackCallCount == 3)
+ context.test.source().cancel();
+ return true;
+ }), G_PRIORITY_DEFAULT,
+ [&] {
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.destroyCallbackCalled = true;
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.test.delayedFinish();
+ context.test.runLoop();
+
+ EXPECT_TRUE(!context.test.source().isActive());
+ EXPECT_EQ(3, context.callbackCallCount);
+ EXPECT_TRUE(context.destroyCallbackCalled);
+ }
+}
+
+TEST(WTF_GMainLoopSource, DestroyCallbacksAfterCancellingDuringDispatch)
+{
+ struct TestingContext {
+ GMainLoopSourceTest<GMainLoopSource> test;
+ unsigned callbackCallCount= 0;
+ bool destroyCallbackCalled = false;
+ };
+ destroyCallbacksAfterCancellingDuringDispatch<TestingContext>();
+
+ struct ThreadSafeTestingContext {
+ GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
+ unsigned callbackCallCount= 0;
+ bool destroyCallbackCalled = false;
+ };
+ destroyCallbacksAfterCancellingDuringDispatch<ThreadSafeTestingContext>();
+}
+
+template <typename T>
+static void destroyCallbacksAfterReschedulingDuringDispatch()
+{
+ {
+ T context;
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.test.source().schedule("[Test] BaseCallback",
+ [&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ context.firstCallbackCallCount++;
+ context.test.source().schedule("[Test] ReschedulingCallback",
+ [&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ context.secondCallbackCallCount++;
+ }, G_PRIORITY_DEFAULT,
+ [&] {
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.secondDestroyCallbackCalled = true;
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+ }, G_PRIORITY_DEFAULT,
+ [&] {
+ // At this point the GMainLoopSource has been rescheduled, ergo the Scheduled status.
+ EXPECT_TRUE(context.test.source().isScheduled());
+ context.firstDestroyCallbackCalled = true;
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.test.delayedFinish();
+ context.test.runLoop();
+
+ EXPECT_TRUE(!context.test.source().isActive());
+ EXPECT_EQ(1, context.firstCallbackCallCount);
+ EXPECT_TRUE(context.firstDestroyCallbackCalled);
+ EXPECT_EQ(1, context.secondCallbackCallCount);
+ EXPECT_TRUE(context.secondDestroyCallbackCalled);
+ }
+
+ {
+ T context;
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.test.source().schedule("[Test] BaseCallback",
+ std::function<bool ()>([&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ context.firstCallbackCallCount++;
+ context.test.source().schedule("[Test] ReschedulingCallback",
+ std::function<bool ()>([&] {
+ EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
+ context.secondCallbackCallCount++;
+ return context.secondCallbackCallCount != 3;
+ }), G_PRIORITY_DEFAULT,
+ [&] {
+ EXPECT_TRUE(!context.test.source().isActive());
+ context.secondDestroyCallbackCalled = true;
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+ return true;
+ }), G_PRIORITY_DEFAULT,
+ [&] {
+ // At this point the GMainLoopSource has been rescheduled, ergo the Scheduled status.
+ EXPECT_TRUE(context.test.source().isScheduled());
+ context.firstDestroyCallbackCalled = true;
+ });
+ EXPECT_TRUE(context.test.source().isScheduled());
+
+ context.test.delayedFinish();
+ context.test.runLoop();
+
+ EXPECT_TRUE(!context.test.source().isActive());
+ EXPECT_EQ(1, context.firstCallbackCallCount);
+ EXPECT_TRUE(context.firstDestroyCallbackCalled);
+ EXPECT_EQ(3, context.secondCallbackCallCount);
+ EXPECT_TRUE(context.secondDestroyCallbackCalled);
+ }
+}
+
+TEST(WTF_GMainLoopSource, DestroyCallbacksAfterReschedulingDuringDispatch)
+{
+ struct TestingContext {
+ GMainLoopSourceTest<GMainLoopSource> test;
+ unsigned firstCallbackCallCount = 0;
+ bool firstDestroyCallbackCalled = false;
+ unsigned secondCallbackCallCount = 0;
+ bool secondDestroyCallbackCalled = false;
+ };
+ destroyCallbacksAfterReschedulingDuringDispatch<TestingContext>();
+
+ struct ThreadSafeTestingContext {
+ GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
+ unsigned firstCallbackCallCount = 0;
+ bool firstDestroyCallbackCalled = false;
+ unsigned secondCallbackCallCount = 0;
+ bool secondDestroyCallbackCalled = false;
+ };
+ destroyCallbacksAfterReschedulingDuringDispatch<ThreadSafeTestingContext>();
+}
+
+TEST(WTF_GMainLoopSource, DeleteOnDestroySources)
+{
+ // Testing the delete-on-destroy sources is very limited. There's no good way
+ // of testing that the GMainLoopSource objects are deleted when their GSource
+ // is destroyed.
+
+ struct TestingContext {
+ GMainLoopSourceTest<GMainLoopSource> test;
+ unsigned callbackCallCount = 0;
+ bool destroyCallbackCalled = false;
+ } context;
+
+ {
+ TestingContext context;
+
+ GMainLoopSource::scheduleAndDeleteOnDestroy("[Test] DeleteOnDestroy",
+ [&] {
+ context.callbackCallCount++;
+ }, G_PRIORITY_DEFAULT,
+ [&] {
+ EXPECT_FALSE(context.destroyCallbackCalled);
+ context.destroyCallbackCalled = true;
+ });
+
+ context.test.delayedFinish();
+ context.test.runLoop();
+ EXPECT_EQ(1, context.callbackCallCount);
+ EXPECT_TRUE(context.destroyCallbackCalled);
+ }
+
+ {
+ TestingContext context;
+
+ GMainLoopSource::scheduleAndDeleteOnDestroy("[Test] DeleteOnDestroy",
+ std::function<bool ()>([&] {
+ context.callbackCallCount++;
+ return context.callbackCallCount != 3;
+ }), G_PRIORITY_DEFAULT,
+ [&] {
+ EXPECT_FALSE(context.destroyCallbackCalled);
+ context.destroyCallbackCalled = true;
+ });
+
+ context.test.delayedFinish();
+ context.test.runLoop();
+ EXPECT_EQ(3, context.callbackCallCount);
+ EXPECT_TRUE(context.destroyCallbackCalled);
+ }
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/glib/GUniquePtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/glib/GUniquePtr.cpp
new file mode 100644
index 000000000..11f02c7b0
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/glib/GUniquePtr.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+inline std::ostringstream& log()
+{
+ static std::ostringstream log;
+ return log;
+}
+
+inline std::string takeLogStr()
+{
+ std::string string = log().str();
+ log().str("");
+ return string;
+}
+
+static void (* _g_free)(void*) = g_free;
+#define g_free(x) \
+ log() << "g_free(" << ptr << ");"; \
+ _g_free(x);
+
+static void (* _g_error_free)(GError*) = g_error_free;
+#define g_error_free(x) \
+ log() << "g_error_free(" << ptr << ");"; \
+ _g_error_free(x);
+
+static void (* _g_list_free)(GList*) = g_list_free;
+#define g_list_free(x) \
+ log() << "g_list_free(" << ptr << ");"; \
+ _g_list_free(x);
+
+static void (* _g_slist_free)(GSList*) = g_slist_free;
+#define g_slist_free(x) \
+ log() << "g_slist_free(" << ptr << ");"; \
+ _g_slist_free(x);
+
+static void (* _g_pattern_spec_free)(GPatternSpec*) = g_pattern_spec_free;
+#define g_pattern_spec_free(x) \
+ log() << "g_pattern_spec_free(" << ptr << ");"; \
+ _g_pattern_spec_free(x);
+
+static void (* _g_dir_close)(GDir*) = g_dir_close;
+#define g_dir_close(x) \
+ log() << "g_dir_close(" << ptr << ");"; \
+ _g_dir_close(x);
+
+static void (* _g_timer_destroy)(GTimer*) = g_timer_destroy;
+#define g_timer_destroy(x) \
+ log() << "g_timer_destroy(" << ptr << ");"; \
+ _g_timer_destroy(x);
+
+static void (* _g_key_file_free)(GKeyFile*) = g_key_file_free;
+#define g_key_file_free(x) \
+ log() << "g_key_file_free(" << ptr << ");"; \
+ _g_key_file_free(x);
+
+#include <wtf/glib/GUniquePtr.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF_GUniquePtr, Basic)
+{
+ std::ostringstream actual;
+
+ {
+ GUniquePtr<char> a(g_strdup("a"));
+ actual << "g_free(" << a.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+
+ {
+ GUniquePtr<GError> a(g_error_new_literal(G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "a"));
+ actual << "g_error_free(" << a.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+
+ {
+ GUniquePtr<GList> a(g_list_prepend(nullptr, g_strdup("a")));
+ actual << "g_list_free(" << a.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+
+ {
+ GUniquePtr<GSList> a(g_slist_prepend(nullptr, g_strdup("a")));
+ actual << "g_slist_free(" << a.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+
+ {
+ GUniquePtr<GPatternSpec> a(g_pattern_spec_new("a"));
+ actual << "g_pattern_spec_free(" << a.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+
+ {
+ GUniquePtr<GDir> a(g_dir_open("/tmp", 0, nullptr));
+ actual << "g_dir_close(" << a.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+
+ {
+ GUniquePtr<GTimer> a(g_timer_new());
+ actual << "g_timer_destroy(" << a.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+
+ {
+ GUniquePtr<GKeyFile> a(g_key_file_new());
+ actual << "g_key_file_free(" << a.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+}
+
+static void returnOutChar(char** outChar)
+{
+ *outChar = g_strdup("a");
+}
+
+TEST(WTF_GUniquePtr, OutPtr)
+{
+ std::ostringstream actual;
+
+ {
+ GUniqueOutPtr<char> a;
+ ASSERT_EQ(nullptr, a.get());
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+
+ {
+ GUniqueOutPtr<char> a;
+ returnOutChar(&a.outPtr());
+ actual << "g_free(" << a.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+
+ {
+ GUniqueOutPtr<char> a;
+ returnOutChar(&a.outPtr());
+ actual << "g_free(" << a.get() << ");";
+ returnOutChar(&a.outPtr());
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+ actual << "g_free(" << a.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+
+ {
+ GUniqueOutPtr<char> a;
+ returnOutChar(&a.outPtr());
+ GUniquePtr<char> b = a.release();
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual << "g_free(" << b.get() << ");";
+ }
+ ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str());
+ actual.str("");
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/glib/WorkQueueGLib.cpp b/Tools/TestWebKitAPI/Tests/WTF/glib/WorkQueueGLib.cpp
new file mode 100644
index 000000000..9923d6580
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WTF/glib/WorkQueueGLib.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "Test.h"
+#include <gio/gio.h>
+#include <thread>
+#include <wtf/Condition.h>
+#include <wtf/Lock.h>
+#include <wtf/WorkQueue.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF_WorkQueue, AsyncIO)
+{
+ struct TestingContext {
+ Lock m_lock;
+ Condition m_testCompleted;
+ GMainContext* m_mainContext;
+ } context;
+
+ auto queue = WorkQueue::create("com.apple.WebKit.Test.AsyncIO");
+ context.m_mainContext = g_main_context_default();
+ EXPECT_FALSE(g_main_context_get_thread_default());
+
+ GUniquePtr<char> currentDirectory(g_get_current_dir());
+ GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(currentDirectory.get()));
+
+ LockHolder locker(context.m_lock);
+ queue->dispatch([&](void) {
+ EXPECT_TRUE(g_main_context_get_thread_default());
+ EXPECT_TRUE(g_main_context_get_thread_default() != context.m_mainContext);
+ context.m_mainContext = g_main_context_get_thread_default();
+ g_file_query_info_async(file.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, nullptr,
+ [](GObject*, GAsyncResult*, gpointer userData) {
+ TestingContext* context = static_cast<TestingContext*>(userData);
+ LockHolder locker(context->m_lock);
+ EXPECT_EQ(g_main_context_get_thread_default(), context->m_mainContext);
+ context->m_testCompleted.notifyOne();
+ }, &context);
+ });
+
+ context.m_testCompleted.wait(context.m_lock);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/CSSParser.cpp b/Tools/TestWebKitAPI/Tests/WebCore/CSSParser.cpp
new file mode 100644
index 000000000..25c450933
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/CSSParser.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 Igalia, S.L. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <WebCore/CSSParser.h>
+#include <WebCore/CSSValueList.h>
+#include <WebCore/StyleProperties.h>
+
+namespace TestWebKitAPI {
+
+using namespace WebCore;
+
+#if ENABLE(CSS_GRID_LAYOUT)
+static unsigned computeNumberOfTracks(CSSValueList& valueList)
+{
+ unsigned numberOfTracks = 0;
+ for (const auto& value : valueList) {
+ if (value->isGridLineNamesValue())
+ continue;
+ ++numberOfTracks;
+ }
+ return numberOfTracks;
+}
+#endif
+
+TEST(CSSPropertyParserTest, GridTrackLimits)
+{
+#if ENABLE(CSS_GRID_LAYOUT)
+ struct {
+ const CSSPropertyID propertyID;
+ const char* input;
+ const size_t output;
+ } testCases[] = {
+ {CSSPropertyWebkitGridTemplateColumns, "-webkit-grid-template-columns: repeat(999999, 20px);", 999999},
+ {CSSPropertyWebkitGridTemplateRows, "-webkit-grid-template-rows: repeat(999999, 20px);", 999999},
+ {CSSPropertyWebkitGridTemplateColumns, "-webkit-grid-template-columns: repeat(1000000, 10%);", 1000000},
+ {CSSPropertyWebkitGridTemplateRows, "-webkit-grid-template-rows: repeat(1000000, 10%);", 1000000},
+ {CSSPropertyWebkitGridTemplateColumns, "-webkit-grid-template-columns: repeat(1000000, [first] -webkit-min-content [last]);", 1000000},
+ {CSSPropertyWebkitGridTemplateRows, "-webkit-grid-template-rows: repeat(1000000, [first] -webkit-min-content [last]);", 1000000},
+ {CSSPropertyWebkitGridTemplateColumns, "-webkit-grid-template-columns: repeat(1000001, auto);", 1000000},
+ {CSSPropertyWebkitGridTemplateRows, "-webkit-grid-template-rows: repeat(1000001, auto);", 1000000},
+ {CSSPropertyWebkitGridTemplateColumns, "-webkit-grid-template-columns: repeat(400000, 2em minmax(10px, -webkit-max-content) 0.5fr);", 999999},
+ {CSSPropertyWebkitGridTemplateRows, "-webkit-grid-template-rows: repeat(400000, 2em minmax(10px, -webkit-max-content) 0.5fr);", 999999},
+ {CSSPropertyWebkitGridTemplateColumns, "-webkit-grid-template-columns: repeat(600000, [first] 3vh 10% 2fr [nav] 10px auto 1fr 6em [last]);", 999999},
+ {CSSPropertyWebkitGridTemplateRows, "-webkit-grid-template-rows: repeat(600000, [first] 3vh 10% 2fr [nav] 10px auto 1fr 6em [last]);", 999999},
+ {CSSPropertyWebkitGridTemplateColumns, "-webkit-grid-template-columns: repeat(100000000000000000000, 10% 1fr);", 1000000},
+ {CSSPropertyWebkitGridTemplateRows, "-webkit-grid-template-rows: repeat(100000000000000000000, 10% 1fr);", 1000000},
+ {CSSPropertyWebkitGridTemplateColumns, "-webkit-grid-template-columns: repeat(100000000000000000000, 10% 5em 1fr auto auto 15px -webkit-min-content);", 999999},
+ {CSSPropertyWebkitGridTemplateRows, "-webkit-grid-template-rows: repeat(100000000000000000000, 10% 5em 1fr auto auto 15px -webkit-min-content);", 999999},
+ };
+
+ CSSParser parser(strictCSSParserContext());
+ RefPtr<MutableStyleProperties> properties = MutableStyleProperties::create();
+
+ for (auto& testCase : testCases) {
+ ASSERT_TRUE(parser.parseDeclaration(properties.get(), testCase.input, nullptr, nullptr));
+ RefPtr<CSSValue> value = properties->getPropertyCSSValue(testCase.propertyID);
+
+ ASSERT_TRUE(value->isValueList());
+ EXPECT_EQ(computeNumberOfTracks(*downcast<CSSValueList>(value.get())), testCase.output);
+ }
+#endif // ENABLE(CSS_GRID_LAYOUT)
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/CalculationValue.cpp b/Tools/TestWebKitAPI/Tests/WebCore/CalculationValue.cpp
new file mode 100644
index 000000000..e6cd15b52
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/CalculationValue.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <WebCore/CalculationValue.h>
+
+namespace TestWebKitAPI {
+
+static unsigned deletionCount;
+
+class CalculationDeletionTestNode : public WebCore::CalcExpressionNode {
+public:
+ virtual ~CalculationDeletionTestNode()
+ {
+ ++deletionCount;
+ }
+
+ virtual float evaluate(float) const override { return 0; }
+ bool operator==(const CalcExpressionNode&) const override { ASSERT_NOT_REACHED(); return false; }
+};
+
+static Ref<WebCore::CalculationValue> createTestValue()
+{
+ auto node = std::make_unique<CalculationDeletionTestNode>();
+ return WebCore::CalculationValue::create(WTF::move(node), WebCore::CalculationRangeAll);
+}
+
+TEST(CalculationValue, LengthConstruction)
+{
+ RefPtr<WebCore::CalculationValue> value = createTestValue();
+
+ EXPECT_EQ(1U, value->refCount());
+
+ {
+ WebCore::Length length(*value);
+ EXPECT_EQ(2U, value->refCount());
+ }
+
+ EXPECT_EQ(1U, value->refCount());
+
+ {
+ WebCore::Length lengthA(*value);
+ EXPECT_EQ(2U, value->refCount());
+ WebCore::Length lengthB(lengthA);
+ EXPECT_EQ(2U, value->refCount());
+ }
+
+ EXPECT_EQ(1U, value->refCount());
+
+ {
+ WebCore::Length lengthC(*value);
+ EXPECT_EQ(2U, value->refCount());
+ WebCore::Length lengthD(WTF::move(lengthC));
+ EXPECT_EQ(2U, value->refCount());
+ }
+
+ EXPECT_EQ(1U, value->refCount());
+
+ EXPECT_EQ(0U, deletionCount);
+ value = nullptr;
+ EXPECT_EQ(1U, deletionCount);
+ deletionCount = 0;
+}
+
+TEST(CalculationValue, LengthConstructionReleasedValue)
+{
+ RefPtr<WebCore::CalculationValue> value = createTestValue();
+
+ EXPECT_EQ(1U, value->refCount());
+
+ {
+ auto* rawValue = value.get();
+ WebCore::Length length(value.releaseNonNull());
+ EXPECT_EQ(1U, rawValue->refCount());
+
+ EXPECT_EQ(0U, deletionCount);
+ }
+
+ EXPECT_EQ(1U, deletionCount);
+ deletionCount = 0;
+
+ value = createTestValue();
+
+ {
+ auto* rawValue = value.get();
+ WebCore::Length lengthA(value.releaseNonNull());
+ EXPECT_EQ(1U, rawValue->refCount());
+ WebCore::Length lengthB(lengthA);
+ EXPECT_EQ(1U, rawValue->refCount());
+
+ EXPECT_EQ(0U, deletionCount);
+ }
+
+ EXPECT_EQ(1U, deletionCount);
+ deletionCount = 0;
+
+ value = createTestValue();
+
+ {
+ auto* rawValue = value.get();
+ WebCore::Length lengthC(value.releaseNonNull());
+ EXPECT_EQ(1U, rawValue->refCount());
+ WebCore::Length lengthD(WTF::move(lengthC));
+ EXPECT_EQ(1U, rawValue->refCount());
+
+ EXPECT_EQ(0U, deletionCount);
+ }
+
+ EXPECT_EQ(1U, deletionCount);
+ deletionCount = 0;
+}
+
+TEST(CalculationValue, LengthAssignment)
+{
+ RefPtr<WebCore::CalculationValue> value = createTestValue();
+
+ EXPECT_EQ(1U, value->refCount());
+
+ {
+ WebCore::Length lengthA(*value);
+ EXPECT_EQ(2U, value->refCount());
+ WebCore::Length lengthB;
+ lengthB = lengthA;
+ EXPECT_EQ(2U, value->refCount());
+ }
+
+ EXPECT_EQ(1U, value->refCount());
+
+ {
+ WebCore::Length lengthC(*value);
+ EXPECT_EQ(2U, value->refCount());
+ WebCore::Length lengthD;
+ lengthD = WTF::move(lengthC);
+ EXPECT_EQ(2U, value->refCount());
+ }
+
+ EXPECT_EQ(1U, value->refCount());
+
+ EXPECT_EQ(0U, deletionCount);
+ value = nullptr;
+ EXPECT_EQ(1U, deletionCount);
+ deletionCount = 0;
+
+ value = createTestValue();
+ RefPtr<WebCore::CalculationValue> value2 = createTestValue();
+
+ EXPECT_EQ(1U, value->refCount());
+ EXPECT_EQ(1U, value2->refCount());
+
+ {
+ WebCore::Length lengthE(*value);
+ EXPECT_EQ(2U, value->refCount());
+ WebCore::Length lengthF(*value2);
+ EXPECT_EQ(2U, value2->refCount());
+ lengthE = lengthF;
+ EXPECT_EQ(1U, value->refCount());
+ EXPECT_EQ(2U, value2->refCount());
+ }
+
+ EXPECT_EQ(1U, value->refCount());
+ EXPECT_EQ(1U, value2->refCount());
+
+ {
+ WebCore::Length lengthG(*value);
+ EXPECT_EQ(2U, value->refCount());
+ WebCore::Length lengthH(*value2);
+ EXPECT_EQ(2U, value2->refCount());
+ lengthG = WTF::move(lengthH);
+ EXPECT_EQ(1U, value->refCount());
+ EXPECT_EQ(2U, value2->refCount());
+ }
+
+ EXPECT_EQ(0U, deletionCount);
+ value = nullptr;
+ EXPECT_EQ(1U, deletionCount);
+ value2 = nullptr;
+ EXPECT_EQ(2U, deletionCount);
+ deletionCount = 0;
+}
+
+TEST(CalculationValue, LengthAssignmentReleasedValue)
+{
+ RefPtr<WebCore::CalculationValue> value = createTestValue();
+
+ {
+ auto* rawValue = value.get();
+ WebCore::Length lengthA(value.releaseNonNull());
+ EXPECT_EQ(1U, rawValue->refCount());
+ WebCore::Length lengthB;
+ lengthB = lengthA;
+ EXPECT_EQ(1U, rawValue->refCount());
+
+ EXPECT_EQ(0U, deletionCount);
+ }
+
+ EXPECT_EQ(1U, deletionCount);
+ deletionCount = 0;
+
+ value = createTestValue();
+
+ {
+ auto* rawValue = value.get();
+ WebCore::Length lengthC(value.releaseNonNull());
+ EXPECT_EQ(1U, rawValue->refCount());
+ WebCore::Length lengthD;
+ lengthD = WTF::move(lengthC);
+ EXPECT_EQ(1U, rawValue->refCount());
+
+ EXPECT_EQ(0U, deletionCount);
+ }
+
+ EXPECT_EQ(1U, deletionCount);
+ deletionCount = 0;
+
+ value = createTestValue();
+ RefPtr<WebCore::CalculationValue> value2 = createTestValue();
+
+ EXPECT_EQ(1U, value->refCount());
+ EXPECT_EQ(1U, value2->refCount());
+
+ {
+ auto* rawValue = value.get();
+ WebCore::Length lengthE(value.releaseNonNull());
+ EXPECT_EQ(1U, rawValue->refCount());
+ auto* rawValue2 = value2.get();
+ WebCore::Length lengthF(value2.releaseNonNull());
+ EXPECT_EQ(1U, rawValue2->refCount());
+
+ lengthE = lengthF;
+ EXPECT_EQ(1U, deletionCount);
+ EXPECT_EQ(1U, rawValue2->refCount());
+ }
+
+ EXPECT_EQ(2U, deletionCount);
+ deletionCount = 0;
+
+ value = createTestValue();
+ value2 = createTestValue();
+
+ EXPECT_EQ(1U, value->refCount());
+ EXPECT_EQ(1U, value2->refCount());
+
+ {
+ auto* rawValue = value.get();
+ WebCore::Length lengthG(value.releaseNonNull());
+ EXPECT_EQ(1U, rawValue->refCount());
+ auto* rawValue2 = value2.get();
+ WebCore::Length lengthH(value2.releaseNonNull());
+ EXPECT_EQ(1U, rawValue2->refCount());
+
+ lengthG = WTF::move(lengthH);
+ EXPECT_EQ(1U, deletionCount);
+ EXPECT_EQ(1U, rawValue2->refCount());
+ }
+
+ EXPECT_EQ(2U, deletionCount);
+ deletionCount = 0;
+}
+
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp b/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp
new file mode 100644
index 000000000..608da59fc
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp
@@ -0,0 +1,2793 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "PlatformUtilities.h"
+#include <JavaScriptCore/InitializeThreading.h>
+#include <WebCore/CombinedURLFilters.h>
+#include <WebCore/ContentExtensionCompiler.h>
+#include <WebCore/ContentExtensionError.h>
+#include <WebCore/ContentExtensionsBackend.h>
+#include <WebCore/DFA.h>
+#include <WebCore/DFABytecodeCompiler.h>
+#include <WebCore/DFABytecodeInterpreter.h>
+#include <WebCore/NFA.h>
+#include <WebCore/NFAToDFA.h>
+#include <WebCore/ResourceLoadInfo.h>
+#include <WebCore/URL.h>
+#include <WebCore/URLFilterParser.h>
+#include <wtf/MainThread.h>
+#include <wtf/RunLoop.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+namespace ContentExtensions {
+inline std::ostream& operator<<(std::ostream& os, const ActionType& action)
+{
+ switch (action) {
+ case ActionType::BlockLoad:
+ return os << "ContentFilterAction::BlockLoad";
+ case ActionType::BlockCookies:
+ return os << "ContentFilterAction::BlockCookies";
+ case ActionType::CSSDisplayNoneSelector:
+ return os << "ContentFilterAction::CSSDisplayNone";
+ case ActionType::CSSDisplayNoneStyleSheet:
+ return os << "ContentFilterAction::CSSDisplayNoneStyleSheet";
+ case ActionType::IgnorePreviousRules:
+ return os << "ContentFilterAction::IgnorePreviousRules";
+ case ActionType::InvalidAction:
+ return os << "ContentFilterAction::InvalidAction";
+ }
+}
+}
+}
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+class ContentExtensionTest : public testing::Test {
+public:
+ virtual void SetUp()
+ {
+ WTF::initializeMainThread();
+ JSC::initializeThreading();
+ RunLoop::initializeMainRunLoop();
+ }
+};
+
+struct CompiledContentExtensionData {
+ Vector<ContentExtensions::SerializedActionByte> actions;
+ Vector<ContentExtensions::DFABytecode> filtersWithoutDomains;
+ Vector<ContentExtensions::DFABytecode> filtersWithDomains;
+ Vector<ContentExtensions::DFABytecode> domainFilters;
+};
+
+class InMemoryContentExtensionCompilationClient final : public ContentExtensions::ContentExtensionCompilationClient {
+public:
+ InMemoryContentExtensionCompilationClient(CompiledContentExtensionData& data)
+ : m_data(data)
+ {
+ EXPECT_EQ(data.actions.size(), 0ull);
+ EXPECT_EQ(data.filtersWithoutDomains.size(), 0ull);
+ EXPECT_EQ(data.filtersWithDomains.size(), 0ull);
+ EXPECT_EQ(data.domainFilters.size(), 0ull);
+ }
+
+ virtual void writeActions(Vector<ContentExtensions::SerializedActionByte>&& actions) override
+ {
+ EXPECT_FALSE(finalized);
+ EXPECT_EQ(m_data.actions.size(), 0ull);
+ EXPECT_EQ(m_data.filtersWithoutDomains.size(), 0ull);
+ EXPECT_EQ(m_data.filtersWithDomains.size(), 0ull);
+ EXPECT_EQ(m_data.domainFilters.size(), 0ull);
+ m_data.actions.appendVector(actions);
+ }
+
+ virtual void writeFiltersWithoutDomainsBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) override
+ {
+ EXPECT_FALSE(finalized);
+ EXPECT_EQ(m_data.filtersWithDomains.size(), 0ull);
+ EXPECT_EQ(m_data.domainFilters.size(), 0ull);
+ m_data.filtersWithoutDomains.appendVector(bytecode);
+ }
+
+ virtual void writeFiltersWithDomainsBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) override
+ {
+ EXPECT_FALSE(finalized);
+ EXPECT_EQ(m_data.domainFilters.size(), 0ull);
+ m_data.filtersWithDomains.appendVector(bytecode);
+ }
+
+ virtual void writeDomainFiltersBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) override
+ {
+ EXPECT_FALSE(finalized);
+ m_data.domainFilters.appendVector(bytecode);
+ }
+
+ virtual void finalize() override
+ {
+ finalized = true;
+ }
+
+private:
+ CompiledContentExtensionData& m_data;
+ bool finalized { false };
+};
+
+class InMemoryCompiledContentExtension : public ContentExtensions::CompiledContentExtension {
+public:
+ static RefPtr<InMemoryCompiledContentExtension> createFromFilter(String&& filter)
+ {
+ CompiledContentExtensionData extensionData;
+ InMemoryContentExtensionCompilationClient client(extensionData);
+ auto compilerError = ContentExtensions::compileRuleList(client, WTF::move(filter));
+ if (compilerError) {
+ // Compiling should always succeed here. We have other tests for compile failures.
+ EXPECT_TRUE(false);
+ return nullptr;
+ }
+
+ return InMemoryCompiledContentExtension::create(WTF::move(extensionData));
+ }
+
+ static RefPtr<InMemoryCompiledContentExtension> create(CompiledContentExtensionData&& data)
+ {
+ return adoptRef(new InMemoryCompiledContentExtension(WTF::move(data)));
+ }
+
+ virtual ~InMemoryCompiledContentExtension()
+ {
+ }
+
+ virtual const ContentExtensions::SerializedActionByte* actions() const override { return m_data.actions.data(); }
+ virtual unsigned actionsLength() const override { return m_data.actions.size(); }
+ virtual const ContentExtensions::DFABytecode* filtersWithoutDomainsBytecode() const override { return m_data.filtersWithoutDomains.data(); }
+ virtual unsigned filtersWithoutDomainsBytecodeLength() const override { return m_data.filtersWithoutDomains.size(); }
+ virtual const ContentExtensions::DFABytecode* filtersWithDomainsBytecode() const override { return m_data.filtersWithDomains.data(); }
+ virtual unsigned filtersWithDomainsBytecodeLength() const override { return m_data.filtersWithDomains.size(); }
+ virtual const ContentExtensions::DFABytecode* domainFiltersBytecode() const override { return m_data.domainFilters.data(); }
+ virtual unsigned domainFiltersBytecodeLength() const override { return m_data.domainFilters.size(); }
+
+private:
+ InMemoryCompiledContentExtension(CompiledContentExtensionData&& data)
+ : m_data(WTF::move(data))
+ {
+ }
+
+ CompiledContentExtensionData m_data;
+};
+
+void static testRequest(ContentExtensions::ContentExtensionsBackend contentExtensionsBackend, const ResourceLoadInfo& resourceLoadInfo, Vector<ContentExtensions::ActionType> expectedActions, bool ignorePreviousRules = false)
+{
+ auto actions = contentExtensionsBackend.actionsForResourceLoad(resourceLoadInfo);
+ unsigned expectedSize = actions.size();
+ if (!ignorePreviousRules)
+ expectedSize--; // The last action is applying the compiled stylesheet.
+
+ EXPECT_EQ(expectedActions.size(), expectedSize);
+ if (expectedActions.size() != expectedSize)
+ return;
+
+ for (unsigned i = 0; i < expectedActions.size(); ++i)
+ EXPECT_EQ(expectedActions[i], actions[i].type());
+ if (!ignorePreviousRules)
+ EXPECT_EQ(actions[actions.size() - 1].type(), ContentExtensions::ActionType::CSSDisplayNoneStyleSheet);
+}
+
+static ResourceLoadInfo mainDocumentRequest(const char* url, ResourceType resourceType = ResourceType::Document)
+{
+ return { URL(URL(), url), URL(URL(), url), resourceType };
+}
+
+static ResourceLoadInfo subResourceRequest(const char* url, const char* mainDocumentURL, ResourceType resourceType = ResourceType::Document)
+{
+ return { URL(URL(), url), URL(URL(), mainDocumentURL), resourceType };
+}
+
+ContentExtensions::ContentExtensionsBackend makeBackend(const char* json)
+{
+ auto extension = InMemoryCompiledContentExtension::createFromFilter(json);
+ ContentExtensions::ContentExtensionsBackend backend;
+ backend.addContentExtension("testFilter", extension);
+ return backend;
+}
+
+static Vector<ContentExtensions::NFA> createNFAs(ContentExtensions::CombinedURLFilters& combinedURLFilters)
+{
+ Vector<ContentExtensions::NFA> nfas;
+
+ combinedURLFilters.processNFAs(std::numeric_limits<size_t>::max(), [&](ContentExtensions::NFA&& nfa) {
+ nfas.append(WTF::move(nfa));
+ });
+
+ return nfas;
+}
+
+TEST_F(ContentExtensionTest, Basic)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+}
+
+TEST_F(ContentExtensionTest, SingleCharacter)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^z\"}}]");
+ testRequest(matchBackend, mainDocumentRequest("http://webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("zttp://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"y\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("http://webkit.org/"), { });
+ testRequest(searchBackend, mainDocumentRequest("http://webkit.org/ywebkit"), { ContentExtensions::ActionType::BlockLoad });
+}
+
+TEST_F(ContentExtensionTest, SingleCharacterDisjunction)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^z\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^c\"}}]");
+ testRequest(matchBackend, mainDocumentRequest("http://webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("bttp://webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("cttp://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("dttp://webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("zttp://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"x\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"y\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("http://webkit.org/"), { });
+ testRequest(searchBackend, mainDocumentRequest("http://webkit.org/dwebkit"), { });
+ testRequest(searchBackend, mainDocumentRequest("http://webkit.org/xwebkit"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("http://webkit.org/ywebkit"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("http://webkit.org/zwebkit"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeBasic)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"w[0-9]c\", \"url-filter-is-case-sensitive\":true}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"[A-H][a-z]cko\", \"url-filter-is-case-sensitive\":true}}]");
+
+ testRequest(backend, mainDocumentRequest("http://w3c.org"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("w2c://whatwg.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/w0c"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/wac"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/wAc"), { });
+
+ // Note: URL parsing and canonicalization lowercase the scheme and hostname.
+ testRequest(backend, mainDocumentRequest("Aacko://webkit.org"), { });
+ testRequest(backend, mainDocumentRequest("aacko://webkit.org"), { });
+ testRequest(backend, mainDocumentRequest("http://gCcko.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://gccko.org/"), { });
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/Gecko"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/gecko"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/GEcko"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeExclusionGeneratingUniversalTransition)
+{
+ // Transition of the type ([^X]X) effictively transition on every input.
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[^a]+afoobar\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://w3c.org"), { });
+
+ testRequest(backend, mainDocumentRequest("http://w3c.org/foobafoobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://w3c.org/foobarfoobar"), { });
+ testRequest(backend, mainDocumentRequest("http://w3c.org/FOOBAFOOBAR"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://w3c.org/FOOBARFOOBAR"), { });
+
+ // The character before the "a" prefix cannot be another "a".
+ testRequest(backend, mainDocumentRequest("http://w3c.org/aafoobar"), { });
+ testRequest(backend, mainDocumentRequest("http://w3c.org/Aafoobar"), { });
+ testRequest(backend, mainDocumentRequest("http://w3c.org/aAfoobar"), { });
+ testRequest(backend, mainDocumentRequest("http://w3c.org/AAfoobar"), { });
+}
+
+TEST_F(ContentExtensionTest, PatternStartingWithGroup)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(http://whatwg\\\\.org/)?webkit\134\134.org\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://whatwg.org/webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://whatwg.org/webkit.org"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://whatwg.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://whatwg.org"), { });
+}
+
+TEST_F(ContentExtensionTest, PatternNestedGroups)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/(foo(bar)*)+\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobarbar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foofoobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foob"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foor"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/fobar"), { });
+}
+
+TEST_F(ContentExtensionTest, EmptyGroups)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/foo()bar\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/((me)()(too))\"}}]");
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/me"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/too"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/metoo"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foome"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foomebar"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/mefoo"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/mefootoo"), { });
+}
+
+TEST_F(ContentExtensionTest, QuantifiedEmptyGroups)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/foo()+bar\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/(()*()?(target)()+)\"}}]");
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/me"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/too"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/target"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foome"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foomebar"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/mefoo"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/mefootoo"), { });
+}
+
+TEST_F(ContentExtensionTest, MatchPastEndOfString)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".+\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobarbar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foofoobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foob"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foor"), { ContentExtensions::ActionType::BlockLoad });
+}
+
+TEST_F(ContentExtensionTest, StartOfLineAssertion)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^foobar\"}}]");
+
+ testRequest(backend, mainDocumentRequest("foobar://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("foobars:///foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("foobarfoobar:///foobarfoobarfoobar"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoo"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobarf"), { });
+ testRequest(backend, mainDocumentRequest("http://foobar.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://foobar.org/"), { });
+}
+
+TEST_F(ContentExtensionTest, EndOfLineAssertion)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foobar$\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("file:///foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("file:///foobarfoobarfoobar"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoo"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobarf"), { });
+}
+
+TEST_F(ContentExtensionTest, EndOfLineAssertionWithInvertedCharacterSet)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[^y]$\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/a"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/Ya"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/yFoobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/y"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/Y"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobary"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foobarY"), { });
+}
+
+TEST_F(ContentExtensionTest, DotDoesNotIncludeEndOfLine)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"https://webkit\\\\.org/.\"}}]");
+
+ testRequest(backend, mainDocumentRequest("https://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/A"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/z"), { ContentExtensions::ActionType::BlockLoad });
+}
+
+TEST_F(ContentExtensionTest, PrefixInfixSuffixExactMatch)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"infix\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^prefix\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"suffix$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://exact\\\\.org/$\"}}]");
+
+ testRequest(backend, mainDocumentRequest("infix://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://infix.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/infix"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("prefix://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://prefix.org/"), { });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/prefix"), { });
+
+ testRequest(backend, mainDocumentRequest("https://webkit.org/suffix"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://suffix.org/"), { });
+ testRequest(backend, mainDocumentRequest("suffix://webkit.org/"), { });
+
+ testRequest(backend, mainDocumentRequest("http://exact.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://exact.org/oops"), { });
+}
+
+TEST_F(ContentExtensionTest, DuplicatedMatchAllTermsInVariousFormat)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*.*(.)*(.*)(.+)*(.?)*infix\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"pre(.?)+(.+)?post\"}}]");
+
+ testRequest(backend, mainDocumentRequest("infix://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://infix.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/infix"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("pre://webkit.org/post"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://prepost.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://pre.org/posttail"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://pre.pre/posttail"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://pre.org/posttailpost"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("https://post.org/pre"), { });
+ testRequest(backend, mainDocumentRequest("https://pre.org/pre"), { });
+ testRequest(backend, mainDocumentRequest("https://post.org/post"), { });
+}
+
+TEST_F(ContentExtensionTest, UndistinguishableActionInsidePrefixTree)
+{
+ // In this case, the two actions are undistinguishable. The actions of "prefix" appear inside the prefixtree
+ // ending at "suffix".
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"prefix\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"prefixsuffix\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://prefix.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/prefix"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/aaaprefixaaa"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://prefixsuffix.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/prefixsuffix"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/bbbprefixsuffixbbb"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("http://suffix.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/suffix"), { });
+}
+
+TEST_F(ContentExtensionTest, DistinguishableActionInsidePrefixTree)
+{
+ // In this case, the two actions are distinguishable.
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"prefix\"}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"prefixsuffix\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://prefix.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/prefix"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/aaaprefixaaa"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://prefixsuffix.org/"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/prefixsuffix"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/bbbprefixsuffixbbb"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("http://suffix.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/suffix"), { });
+}
+
+TEST_F(ContentExtensionTest, DistinguishablePrefixAreNotMerged)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foo\\\\.org\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar\\\\.org\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://foo.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://bar.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("http://foor.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://fooar.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://fooba.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://foob.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://foor.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://foar.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://foba.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://fob.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://barf.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://barfo.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://baroo.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://baro.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://baf.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://bafo.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://baoo.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://bao.org/"), { });
+
+ testRequest(backend, mainDocumentRequest("http://foo.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://oo.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://o.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://rgbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://gbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://foo.orgar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://foo.orgr.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://foo.org.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://foo.orgorg/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://foo.orgrg/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://foo.orgg/"), { ContentExtensions::ActionType::BlockLoad });
+}
+
+static void compareContents(const ContentExtensions::DFABytecodeInterpreter::Actions& a, const Vector<uint64_t>& b)
+{
+ EXPECT_EQ(a.size(), b.size());
+ for (unsigned i = 0; i < b.size(); ++i)
+ EXPECT_TRUE(a.contains(b[i]));
+}
+
+TEST_F(ContentExtensionTest, SearchSuffixesWithIdenticalActionAreMerged)
+{
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("foo\\.org", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("ba\\.org", false, 0));
+
+ Vector<ContentExtensions::NFA> nfas = createNFAs(combinedURLFilters);
+ EXPECT_EQ(1ul, nfas.size());
+ EXPECT_EQ(12ul, nfas.first().nodes.size());
+
+ ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first());
+ Vector<ContentExtensions::DFABytecode> bytecode;
+ ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode);
+ compiler.compile();
+ ContentExtensions::DFABytecodeInterpreter interpreter(bytecode.data(), bytecode.size());
+ compareContents(interpreter.interpret("foo.org", 0), { 0 });
+ compareContents(interpreter.interpret("ba.org", 0), { 0 });
+ compareContents(interpreter.interpret("bar.org", 0), { });
+
+ compareContents(interpreter.interpret("paddingfoo.org", 0), { 0 });
+ compareContents(interpreter.interpret("paddingba.org", 0), { 0 });
+ compareContents(interpreter.interpret("paddingbar.org", 0), { });
+}
+
+TEST_F(ContentExtensionTest, SearchSuffixesWithDistinguishableActionAreNotMerged)
+{
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("foo\\.org", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("ba\\.org", false, 1));
+
+ Vector<ContentExtensions::NFA> nfas = createNFAs(combinedURLFilters);
+
+ EXPECT_EQ(1ul, nfas.size());
+ EXPECT_EQ(17ul, nfas.first().nodes.size());
+
+ ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first());
+ Vector<ContentExtensions::DFABytecode> bytecode;
+ ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode);
+ compiler.compile();
+ ContentExtensions::DFABytecodeInterpreter interpreter(bytecode.data(), bytecode.size());
+ compareContents(interpreter.interpret("foo.org", 0), { 0 });
+ compareContents(interpreter.interpret("ba.org", 0), { 1 });
+ compareContents(interpreter.interpret("bar.org", 0), { });
+
+ compareContents(interpreter.interpret("paddingfoo.org", 0), { 0 });
+ compareContents(interpreter.interpret("paddingba.org", 0), { 1 });
+ compareContents(interpreter.interpret("paddingba.orgfoo.org", 0), { 1, 0 });
+ compareContents(interpreter.interpret("paddingbar.org", 0), { });
+}
+
+TEST_F(ContentExtensionTest, DomainTriggers)
+{
+ auto ifDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"webkit.org\"]}}]");
+ testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.organization/test.html"), { });
+
+ auto unlessDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"webkit.org\"]}}]");
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.organization/test.html"), { ContentExtensions::ActionType::BlockLoad });
+
+ auto ifDomainStarBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"*webkit.org\"]}}]");
+ testRequest(ifDomainStarBackend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+ testRequest(ifDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { });
+ testRequest(ifDomainStarBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(ifDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(ifDomainStarBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(ifDomainStarBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { });
+ testRequest(ifDomainStarBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { });
+ testRequest(ifDomainStarBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+ testRequest(ifDomainStarBackend, mainDocumentRequest("http://webkit.organization/test.html"), { });
+
+ auto unlessDomainStarBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"*webkit.org\"]}}]");
+ testRequest(unlessDomainStarBackend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+ testRequest(unlessDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { });
+ testRequest(unlessDomainStarBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(unlessDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { });
+ testRequest(unlessDomainStarBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { });
+ testRequest(unlessDomainStarBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { });
+ testRequest(unlessDomainStarBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { });
+ testRequest(unlessDomainStarBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(unlessDomainStarBackend, mainDocumentRequest("http://webkit.organization/test.html"), { ContentExtensions::ActionType::BlockLoad });
+
+ auto ifSubDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"sub1.webkit.org\"]}}]");
+ testRequest(ifSubDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(ifSubDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { });
+ testRequest(ifSubDomainBackend, mainDocumentRequest("http://sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(ifSubDomainBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { });
+
+ auto ifSubDomainStarBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"*sub1.webkit.org\"]}}]");
+ testRequest(ifSubDomainStarBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(ifSubDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { });
+ testRequest(ifSubDomainStarBackend, mainDocumentRequest("http://sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(ifSubDomainStarBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+
+ auto unlessSubDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"sub1.webkit.org\"]}}]");
+ testRequest(unlessSubDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(unlessSubDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(unlessSubDomainBackend, mainDocumentRequest("http://sub1.webkit.org/test.html"), { });
+ testRequest(unlessSubDomainBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+
+ auto unlessSubDomainStarBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"*sub1.webkit.org\"]}}]");
+ testRequest(unlessSubDomainStarBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(unlessSubDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(unlessSubDomainStarBackend, mainDocumentRequest("http://sub1.webkit.org/test.html"), { });
+ testRequest(unlessSubDomainStarBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { });
+
+ auto combinedBackend1 = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test_block_load\", \"if-domain\":[\"webkit.org\"]}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"test_block_cookies\", \"unless-domain\":[\"webkit.org\"]}}]");
+ testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org"), { });
+ testRequest(combinedBackend1, mainDocumentRequest("http://not_webkit.org"), { });
+ testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org/test_block_load.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_load.html", "http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://webkit.org/"), { });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_load.html", "http://not_webkit.org/"), { });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://not_webkit.org/"), { });
+ testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org/test_block_cookies.html"), { });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_cookies.html", "http://webkit.org/"), { });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://webkit.org/"), { });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_cookies.html", "http://not_webkit.org/path/to/main/document.html"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://not_webkit.org/"), { });
+
+ auto combinedBackend2 = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test_block_load\\\\.html\", \"if-domain\":[\"webkit.org\"]}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"test_block_cookies\\\\.html\", \"unless-domain\":[\"w3c.org\"]}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"test_css\\\\.html\"}}]");
+ testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_css.htm"), { });
+ testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_block_load.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(combinedBackend2, mainDocumentRequest("http://not_webkit.org/test_block_load.html"), { });
+ testRequest(combinedBackend2, mainDocumentRequest("http://not_webkit.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/TEST_CSS.hTmL/test_block_load.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad});
+ testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_block_load.html"), { });
+ testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_block_cookies.html"), { });
+ testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_css.html/test_block_cookies.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(combinedBackend2, mainDocumentRequest("http://not_w3c.org/test_block_cookies.html"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(combinedBackend2, mainDocumentRequest("http://not_w3c.org/test_css.html/test_block_cookies.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
+
+ auto ifDomainWithFlagsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\", \"if-domain\":[\"webkit.org\"],\"resource-type\":[\"image\"]}}]");
+ testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.png", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+ testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.png", ResourceType::Image), { });
+
+ auto unlessDomainWithFlagsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\", \"unless-domain\":[\"webkit.org\"],\"resource-type\":[\"image\"]}}]");
+ testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.png", ResourceType::Image), { });
+ testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+ testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.png", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
+
+ // Domains should not be interepted as regular expressions.
+ auto domainRegexBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"we?bkit.org\"]}}]");
+ testRequest(domainRegexBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(domainRegexBackend, mainDocumentRequest("http://wbkit.org/test.html"), { });
+
+ auto multipleIfDomainsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"webkit.org\", \"w3c.org\"]}}]");
+ testRequest(multipleIfDomainsBackend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+ testRequest(multipleIfDomainsBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(multipleIfDomainsBackend, mainDocumentRequest("http://w3c.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(multipleIfDomainsBackend, mainDocumentRequest("http://whatwg.org/test.html"), { });
+
+ auto multipleUnlessDomainsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"webkit.org\", \"w3c.org\"]}}]");
+ testRequest(multipleUnlessDomainsBackend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+ testRequest(multipleUnlessDomainsBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(multipleUnlessDomainsBackend, mainDocumentRequest("http://w3c.org/test.html"), { });
+ testRequest(multipleUnlessDomainsBackend, mainDocumentRequest("http://whatwg.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+
+ // FIXME: Add and test domain-specific popup-only blocking (with layout tests).
+}
+
+TEST_F(ContentExtensionTest, DomainTriggersAlongMergedActions)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"test\\\\.html\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"webkit.org\"]}},"
+ "{\"action\":{\"type\":\"css-display-none\", \"selector\": \"*\"},\"trigger\":{\"url-filter\":\"trigger-on-scripts\\\\.html\",\"resource-type\":[\"script\"]}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"ignore-previous\",\"resource-type\":[\"image\"]}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"except-this\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/test.html"), { ContentExtensions::ActionType::BlockCookies });
+
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/trigger-on-scripts.html"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/trigger-on-scripts.html"), { });
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/ignore-previous-trigger-on-scripts.html"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ignore-previous-trigger-on-scripts.html"), { });
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/ignore-previous-trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ignore-previous-trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/ignore-previous-trigger-on-scripts.html", ResourceType::Image), { }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ignore-previous-trigger-on-scripts.html", ResourceType::Image), { }, true);
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Image), { }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Image), { }, true);
+
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true);
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true);
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+
+}
+
+TEST_F(ContentExtensionTest, MultipleExtensions)
+{
+ auto extension1 = InMemoryCompiledContentExtension::createFromFilter("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"block_load\"}}]");
+ auto extension2 = InMemoryCompiledContentExtension::createFromFilter("[{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"block_cookies\"}}]");
+ ContentExtensions::ContentExtensionsBackend backend;
+ backend.addContentExtension("testFilter1", extension1);
+ backend.addContentExtension("testFilter2", extension2);
+
+ // These each have two display:none stylesheets. The second one is implied by using the default parameter ignorePreviousRules = false.
+ testRequest(backend, mainDocumentRequest("http://webkit.org"), { ContentExtensions::ActionType::CSSDisplayNoneStyleSheet });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/block_load.html"), { ContentExtensions::ActionType::CSSDisplayNoneStyleSheet, ContentExtensions::ActionType::BlockLoad});
+ testRequest(backend, mainDocumentRequest("http://webkit.org/block_cookies.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet});
+ testRequest(backend, mainDocumentRequest("http://webkit.org/block_load/block_cookies.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet, ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/block_cookies/block_load.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet, ContentExtensions::ActionType::BlockLoad });
+
+ auto ignoreExtension1 = InMemoryCompiledContentExtension::createFromFilter("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"block_load\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"ignore1\"}}]");
+ auto ignoreExtension2 = InMemoryCompiledContentExtension::createFromFilter("[{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"block_cookies\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"ignore2\"}}]");
+ ContentExtensions::ContentExtensionsBackend backendWithIgnore;
+ backendWithIgnore.addContentExtension("testFilter1", ignoreExtension1);
+ backendWithIgnore.addContentExtension("testFilter2", ignoreExtension2);
+
+ testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org"), { ContentExtensions::ActionType::CSSDisplayNoneStyleSheet, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet }, true);
+ testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org/block_load/ignore1.html"), { ContentExtensions::ActionType::CSSDisplayNoneStyleSheet }, true);
+ testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org/block_cookies/ignore1.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet}, true);
+ testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org/block_load/ignore2.html"), { ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet }, true);
+ testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org/block_cookies/ignore2.html"), { ContentExtensions::ActionType::CSSDisplayNoneStyleSheet}, true);
+ testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org/block_load/block_cookies/ignore1/ignore2.html"), { }, true);
+}
+
+TEST_F(ContentExtensionTest, TermsKnownToMatchAnything)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre1.*post1$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre2(.*)post2$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre3(.*)?post3$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre4(.*)+post4$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre5(.*)*post5$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre6(.)*post6$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre7(.+)*post7$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre8(.?)*post8$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre9(.+)?post9$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre0(.?)+post0$\"}}]");
+
+ testRequest(backend, mainDocumentRequest("pre1://webkit.org/post1"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("pre2://webkit.org/post2"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("pre3://webkit.org/post3"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("pre4://webkit.org/post4"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("pre5://webkit.org/post5"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("pre6://webkit.org/post6"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("pre7://webkit.org/post7"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("pre8://webkit.org/post8"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("pre9://webkit.org/post9"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("pre0://webkit.org/post0"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("pre1://webkit.org/post2"), { });
+ testRequest(backend, mainDocumentRequest("pre2://webkit.org/post3"), { });
+ testRequest(backend, mainDocumentRequest("pre3://webkit.org/post4"), { });
+ testRequest(backend, mainDocumentRequest("pre4://webkit.org/post5"), { });
+ testRequest(backend, mainDocumentRequest("pre5://webkit.org/post6"), { });
+ testRequest(backend, mainDocumentRequest("pre6://webkit.org/post7"), { });
+ testRequest(backend, mainDocumentRequest("pre7://webkit.org/post8"), { });
+ testRequest(backend, mainDocumentRequest("pre8://webkit.org/post9"), { });
+ testRequest(backend, mainDocumentRequest("pre9://webkit.org/post0"), { });
+ testRequest(backend, mainDocumentRequest("pre0://webkit.org/post1"), { });
+
+ testRequest(backend, mainDocumentRequest("pre0://webkit.org/post1"), { });
+ testRequest(backend, mainDocumentRequest("pre1://webkit.org/post2"), { });
+ testRequest(backend, mainDocumentRequest("pre2://webkit.org/post3"), { });
+ testRequest(backend, mainDocumentRequest("pre3://webkit.org/post4"), { });
+ testRequest(backend, mainDocumentRequest("pre4://webkit.org/post5"), { });
+ testRequest(backend, mainDocumentRequest("pre5://webkit.org/post6"), { });
+ testRequest(backend, mainDocumentRequest("pre6://webkit.org/post7"), { });
+ testRequest(backend, mainDocumentRequest("pre7://webkit.org/post8"), { });
+ testRequest(backend, mainDocumentRequest("pre8://webkit.org/post9"), { });
+ testRequest(backend, mainDocumentRequest("pre9://webkit.org/post0"), { });
+}
+
+TEST_F(ContentExtensionTest, TrailingDotStar)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foo.*$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar(.*)$\"}}]");
+
+ testRequest(backend, mainDocumentRequest("https://webkit.org/"), { });
+
+ testRequest(backend, mainDocumentRequest("foo://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://foo.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.foo/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("bar://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://bar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.bar/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
+}
+
+TEST_F(ContentExtensionTest, TrailingTermsCarryingNoData)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foob?a?r?\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bazo(ok)?a?$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"cats*$\"}}]");
+
+ testRequest(backend, mainDocumentRequest("https://webkit.org/"), { });
+
+ // Anything is fine after foo.
+ testRequest(backend, mainDocumentRequest("https://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/foob"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/fooc"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/fooba"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/foobar-stuff"), { ContentExtensions::ActionType::BlockLoad });
+
+ // Bazooka has to be at the tail without any character not defined by the filter.
+ testRequest(backend, mainDocumentRequest("https://webkit.org/baz"), { });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/bazo"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/bazoa"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/bazob"), { });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/bazoo"), { });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/bazook"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/bazookb"), { });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/bazooka"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/bazookaa"), { });
+
+ // The pattern must finish with cat, with any number of 's' following it, but no other character.
+ testRequest(backend, mainDocumentRequest("https://cat.org/"), { });
+ testRequest(backend, mainDocumentRequest("https://cats.org/"), { });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/cat"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/cats"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/catss"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/catsss"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/catso"), { });
+}
+
+TEST_F(ContentExtensionTest, UselessTermsMatchingEverythingAreEliminated)
+{
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern(".*web", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.*)web", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.)*web", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.+)*web", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.?)*web", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.+)?web", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.?)+web", false, 0));
+
+ Vector<ContentExtensions::NFA> nfas = createNFAs(combinedURLFilters);
+ EXPECT_EQ(1ul, nfas.size());
+ EXPECT_EQ(7ul, nfas.first().nodes.size());
+
+ ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first());
+ Vector<ContentExtensions::DFABytecode> bytecode;
+ ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode);
+ compiler.compile();
+ ContentExtensions::DFABytecodeInterpreter interpreter(bytecode.data(), bytecode.size());
+ compareContents(interpreter.interpret("eb", 0), { });
+ compareContents(interpreter.interpret("we", 0), { });
+ compareContents(interpreter.interpret("weeb", 0), { });
+ compareContents(interpreter.interpret("web", 0), { 0 });
+ compareContents(interpreter.interpret("wweb", 0), { 0 });
+ compareContents(interpreter.interpret("wwebb", 0), { 0 });
+ compareContents(interpreter.interpret("http://theweb.com/", 0), { 0 });
+}
+
+TEST_F(ContentExtensionTest, LoadType)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[\"third-party\"]}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"whatwg.org\",\"load-type\":[\"first-party\"]}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"alwaysblock.pdf\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org"), { });
+ testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Document}, { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("http://whatwg.org"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, {URL(URL(), "http://whatwg.org"), URL(URL(), "http://not_whatwg.org"), ResourceType::Document}, { });
+
+ testRequest(backend, mainDocumentRequest("http://foobar.org/alwaysblock.pdf"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, {URL(URL(), "http://foobar.org/alwaysblock.pdf"), URL(URL(), "http://not_foobar.org/alwaysblock.pdf"), ResourceType::Document}, { ContentExtensions::ActionType::BlockLoad });
+}
+
+TEST_F(ContentExtensionTest, ResourceType)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"block_all_types.org\",\"resource-type\":[\"document\",\"image\",\"style-sheet\",\"script\",\"font\",\"raw\",\"svg-document\",\"media\",\"popup\"]}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"block_only_images\",\"resource-type\":[\"image\"]}}]");
+
+ testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Document), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::StyleSheet), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Script), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Font), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Raw), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::SVGDocument), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Media), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Popup), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://block_only_images.org", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://block_only_images.org", ResourceType::Document), { });
+}
+
+TEST_F(ContentExtensionTest, ResourceAndLoadType)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"BlockOnlyIfThirdPartyAndScript\",\"resource-type\":[\"script\"],\"load-type\":[\"third-party\"]}}]");
+
+ testRequest(backend, subResourceRequest("http://webkit.org/BlockOnlyIfThirdPartyAndScript.js", "http://webkit.org", ResourceType::Script), { });
+ testRequest(backend, subResourceRequest("http://webkit.org/BlockOnlyIfThirdPartyAndScript.png", "http://not_webkit.org", ResourceType::Image), { });
+ testRequest(backend, subResourceRequest("http://webkit.org/BlockOnlyIfThirdPartyAndScript.js", "http://not_webkit.org", ResourceType::Script), { ContentExtensions::ActionType::BlockLoad });
+}
+
+TEST_F(ContentExtensionTest, ResourceOrLoadTypeMatchingEverything)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"resource-type\":[\"image\"]}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\".*\",\"load-type\":[\"third-party\"]}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\".*\",\"load-type\":[\"first-party\"]}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org"), { }, true);
+ testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Document}, { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Image}, { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
+}
+
+TEST_F(ContentExtensionTest, WideNFA)
+{
+ // Make an NFA with about 1400 nodes that won't be combined.
+ StringBuilder ruleList;
+ ruleList.append('[');
+ for (char c1 = 'A'; c1 <= 'Z'; ++c1) {
+ for (char c2 = 'A'; c2 <= 'C'; ++c2) {
+ for (char c3 = 'A'; c3 <= 'C'; ++c3) {
+ if (c1 != 'A' || c2 != 'A' || c3 != 'A')
+ ruleList.append(',');
+ ruleList.append("{\"action\":{\"type\":\"");
+
+ // Make every other rule ignore-previous-rules to not combine actions.
+ if (!((c1 + c2 + c3) % 2))
+ ruleList.append("ignore-previous-rules");
+ else {
+ ruleList.append("css-display-none");
+ ruleList.append("\",\"selector\":\"");
+ ruleList.append(c1);
+ ruleList.append(c2);
+ ruleList.append(c3);
+ }
+ ruleList.append("\"},\"trigger\":{\"url-filter\":\".*");
+ ruleList.append(c1);
+ ruleList.append(c2);
+ ruleList.append(c3);
+ ruleList.append("\", \"url-filter-is-case-sensitive\":true}}");
+ }
+ }
+ }
+ ruleList.append(']');
+
+ auto backend = makeBackend(ruleList.toString().utf8().data());
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/AAA"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/YAA"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ZAA"), { }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/LAA/AAA"), { }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/LAA/MAA"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+}
+
+#ifdef NDEBUG
+static uint64_t expectedIndex(char c, unsigned position)
+{
+ uint64_t index = c - 'A';
+ for (unsigned i = 1; i < position; ++i)
+ index *= (i == 1) ? ('C' - 'A' + 1) : ('Z' - 'A' + 1);
+ return index;
+}
+#endif
+
+TEST_F(ContentExtensionTest, LargeJumps)
+{
+// A large test like this is necessary to test 24 and 32 bit jumps, but it's so large it times out in debug builds.
+#ifdef NDEBUG
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+
+ uint64_t patternId = 0;
+ for (char c1 = 'A'; c1 <= 'Z'; ++c1) {
+ for (char c2 = 'A'; c2 <= 'Z'; ++c2) {
+ for (char c3 = 'A'; c3 <= 'Z'; ++c3) {
+ for (char c4 = 'A'; c4 <= 'C'; ++c4) {
+ StringBuilder pattern;
+ pattern.append(c1);
+ pattern.append(c2);
+ pattern.append(c3);
+ pattern.append(c4);
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern(pattern.toString(), true, patternId++));
+ }
+ }
+ }
+ }
+
+ Vector<ContentExtensions::NFA> nfas;
+ combinedURLFilters.processNFAs(std::numeric_limits<size_t>::max(), [&](ContentExtensions::NFA&& nfa) {
+ nfas.append(WTF::move(nfa));
+ });
+ EXPECT_EQ(nfas.size(), 1ull);
+
+ Vector<ContentExtensions::DFA> dfas;
+ for (auto& nfa : nfas)
+ dfas.append(ContentExtensions::NFAToDFA::convert(nfa));
+ EXPECT_EQ(dfas.size(), 1ull);
+
+ Vector<ContentExtensions::DFABytecode> combinedBytecode;
+ for (const auto& dfa : dfas) {
+ Vector<ContentExtensions::DFABytecode> bytecode;
+ ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode);
+ compiler.compile();
+ combinedBytecode.appendVector(bytecode);
+ }
+
+ ContentExtensions::DFABytecodeInterpreter interpreter(&combinedBytecode[0], combinedBytecode.size());
+
+ patternId = 0;
+ for (char c1 = 'A'; c1 <= 'Z'; ++c1) {
+ for (char c2 = 'A'; c2 <= 'Z'; ++c2) {
+ for (char c3 = 'A'; c3 <= 'Z'; ++c3) {
+ for (char c4 = 'A'; c4 <= 'C'; ++c4) {
+ StringBuilder pattern;
+ pattern.append(c1);
+ pattern.append(c2);
+ pattern.append(c3);
+ // Test different jumping patterns distributed throughout the DFA:
+ switch ((c1 + c2 + c3 + c4) % 4) {
+ case 0:
+ // This should not match.
+ pattern.append('x');
+ pattern.append(c4);
+ break;
+ case 1:
+ // This should jump back to the root, then match.
+ pattern.append('x');
+ pattern.append(c1);
+ pattern.append(c2);
+ pattern.append(c3);
+ pattern.append(c4);
+ break;
+ case 2:
+ // This should match at the end of the string.
+ pattern.append(c4);
+ break;
+ case 3:
+ // This should match then jump back to the root.
+ pattern.append(c4);
+ pattern.append('x');
+ break;
+ }
+ auto matches = interpreter.interpret(pattern.toString().utf8(), 0);
+ switch ((c1 + c2 + c3 + c4) % 4) {
+ case 0:
+ compareContents(matches, { });
+ break;
+ case 1:
+ case 2:
+ case 3:
+ compareContents(matches, {patternId});
+ break;
+ }
+ patternId++;
+ }
+ }
+ }
+ }
+
+ compareContents(interpreter.interpret("CAAAAx", 0), {expectedIndex('C', 4), expectedIndex('A', 1)});
+ compareContents(interpreter.interpret("KAAAAx", 0), {expectedIndex('K', 4), expectedIndex('A', 1)});
+ compareContents(interpreter.interpret("AKAAAx", 0), {expectedIndex('K', 3), expectedIndex('K', 4)});
+ compareContents(interpreter.interpret("AKxAAAAx", 0), {expectedIndex('A', 1)});
+ compareContents(interpreter.interpret("AKAxAAAAx", 0), {expectedIndex('A', 1)});
+ compareContents(interpreter.interpret("AKAxZKAxZKZxAAAAx", 0), {expectedIndex('A', 1)});
+ compareContents(interpreter.interpret("ZAAAA", 0), {expectedIndex('Z', 4), expectedIndex('A', 1)});
+ compareContents(interpreter.interpret("ZZxZAAAB", 0), {expectedIndex('Z', 4), expectedIndex('B', 1)});
+#endif
+}
+
+TEST_F(ContentExtensionTest, DeepNFA)
+{
+ const unsigned size = 100000;
+
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+
+ // FIXME: DFAToNFA::convert takes way too long on these deep NFAs. We should optimize for that case.
+
+ StringBuilder lotsOfAs;
+ for (unsigned i = 0; i < size; ++i)
+ lotsOfAs.append('A');
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern(lotsOfAs.toString().utf8().data(), false, 0));
+
+ // FIXME: Yarr ought to be able to handle 2MB regular expressions.
+ StringBuilder tooManyAs;
+ for (unsigned i = 0; i < size * 20; ++i)
+ tooManyAs.append('A');
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::YarrError, parser.addPattern(tooManyAs.toString().utf8().data(), false, 0));
+
+ StringBuilder nestedGroups;
+ for (unsigned i = 0; i < size; ++i)
+ nestedGroups.append('(');
+ for (unsigned i = 0; i < size; ++i)
+ nestedGroups.append("B)");
+ // FIXME: Add nestedGroups. Right now it also takes too long. It should be optimized.
+
+ // This should not crash and not timeout.
+ EXPECT_EQ(1ul, createNFAs(combinedURLFilters).size());
+}
+
+void checkCompilerError(const char* json, std::error_code expectedError)
+{
+ CompiledContentExtensionData extensionData;
+ InMemoryContentExtensionCompilationClient client(extensionData);
+ std::error_code compilerError = ContentExtensions::compileRuleList(client, json);
+ EXPECT_EQ(compilerError.value(), expectedError.value());
+ if (compilerError.value())
+ EXPECT_STREQ(compilerError.category().name(), expectedError.category().name());
+}
+
+TEST_F(ContentExtensionTest, MatchesEverything)
+{
+ // Only css-display-none rules with triggers that match everything, no domain rules, and no flags
+ // should go in the global display:none stylesheet. css-display-none rules with domain rules or flags
+ // are applied separately on pages where they apply.
+ auto backend1 = makeBackend("[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]");
+ EXPECT_TRUE(nullptr != backend1.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter")));
+ testRequest(backend1, mainDocumentRequest("http://webkit.org"), { }); // Selector is in global stylesheet.
+
+ auto backend2 = makeBackend("[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\",\"if-domain\":[\"webkit.org\"]}}]");
+ EXPECT_EQ(nullptr, backend2.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter")));
+ testRequest(backend2, mainDocumentRequest("http://webkit.org"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(backend2, mainDocumentRequest("http://w3c.org"), { });
+
+ auto backend3 = makeBackend("[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\",\"unless-domain\":[\"webkit.org\"]}}]");
+ EXPECT_EQ(nullptr, backend3.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter")));
+ testRequest(backend3, mainDocumentRequest("http://webkit.org"), { });
+ testRequest(backend3, mainDocumentRequest("http://w3c.org"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+
+ auto backend4 = makeBackend("[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\",\"load-type\":[\"third-party\"]}}]");
+ EXPECT_EQ(nullptr, backend4.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter")));
+ testRequest(backend4, mainDocumentRequest("http://webkit.org"), { });
+ testRequest(backend4, subResourceRequest("http://not_webkit.org", "http://webkit.org"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+
+ // css-display-none rules after ignore-previous-rules should not be put in the default stylesheet.
+ auto backend5 = makeBackend("[{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\".*\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]");
+ EXPECT_EQ(nullptr, backend5.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter")));
+ testRequest(backend5, mainDocumentRequest("http://webkit.org"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }, true);
+
+ auto backend6 = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"if-domain\":[\"webkit.org\",\"*w3c.org\"],\"resource-type\":[\"document\",\"script\"]}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"ignore\",\"if-domain\":[\"*webkit.org\",\"w3c.org\"]}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\".*\",\"unless-domain\":[\"webkit.org\",\"whatwg.org\"],\"resource-type\":[\"script\",\"image\"],\"load-type\":[\"third-party\"]}}]");
+ EXPECT_EQ(nullptr, backend6.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter")));
+ testRequest(backend6, mainDocumentRequest("http://webkit.org"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend6, mainDocumentRequest("http://w3c.org"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend6, mainDocumentRequest("http://whatwg.org"), { });
+ testRequest(backend6, mainDocumentRequest("http://sub.webkit.org"), { });
+ testRequest(backend6, mainDocumentRequest("http://sub.w3c.org"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend6, mainDocumentRequest("http://sub.whatwg.org"), { });
+ testRequest(backend6, mainDocumentRequest("http://webkit.org/ignore"), { }, true);
+ testRequest(backend6, mainDocumentRequest("http://w3c.org/ignore"), { }, true);
+ testRequest(backend6, mainDocumentRequest("http://whatwg.org/ignore"), { });
+ testRequest(backend6, mainDocumentRequest("http://sub.webkit.org/ignore"), { }, true);
+ testRequest(backend6, mainDocumentRequest("http://sub.w3c.org/ignore"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend6, mainDocumentRequest("http://sub.whatwg.org/ignore"), { });
+ testRequest(backend6, subResourceRequest("http://example.com/image.png", "http://webkit.org/", ResourceType::Image), { });
+ testRequest(backend6, subResourceRequest("http://example.com/image.png", "http://w3c.org/", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend6, subResourceRequest("http://example.com/doc.html", "http://webkit.org/", ResourceType::Document), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend6, subResourceRequest("http://example.com/script.js", "http://webkit.org/", ResourceType::Script), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend6, subResourceRequest("http://example.com/script.js", "http://w3c.org/", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend6, subResourceRequest("http://example.com/script.js", "http://example.com/", ResourceType::Script), { });
+ testRequest(backend6, subResourceRequest("http://example.com/ignore/image.png", "http://webkit.org/", ResourceType::Image), { }, true);
+ testRequest(backend6, subResourceRequest("http://example.com/ignore/image.png", "http://example.com/", ResourceType::Image), { });
+ testRequest(backend6, subResourceRequest("http://example.com/ignore/image.png", "http://example.org/", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend6, subResourceRequest("http://example.com/doc.html", "http://example.org/", ResourceType::Document), { });
+ testRequest(backend6, subResourceRequest("http://example.com/", "http://example.com/", ResourceType::Font), { });
+ testRequest(backend6, subResourceRequest("http://example.com/ignore", "http://webkit.org/", ResourceType::Image), { }, true);
+ testRequest(backend6, subResourceRequest("http://example.com/ignore", "http://webkit.org/", ResourceType::Font), { }, true);
+ testRequest(backend6, subResourceRequest("http://example.com/", "http://example.com/", ResourceType::Script), { });
+ testRequest(backend6, subResourceRequest("http://example.com/ignore", "http://example.com/", ResourceType::Script), { });
+}
+
+TEST_F(ContentExtensionTest, InvalidJSON)
+{
+ checkCompilerError("[", ContentExtensions::ContentExtensionError::JSONInvalid);
+ checkCompilerError("123", ContentExtensions::ContentExtensionError::JSONTopLevelStructureNotAnObject);
+ checkCompilerError("{}", ContentExtensions::ContentExtensionError::JSONTopLevelStructureNotAnArray);
+ // FIXME: Add unit test for JSONInvalidRule if that is possible to hit.
+ checkCompilerError("[]", ContentExtensions::ContentExtensionError::JSONContainsNoRules);
+
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":5}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTrigger);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"\"}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidURLFilterInTrigger);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":{}}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidURLFilterInTrigger);
+
+ // FIXME: Add unit test for JSONInvalidObjectInTriggerFlagsArray if that is possible to hit.
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":{}}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[\"invalid\"]}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[5]}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":5}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":\"first-party\"}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":null}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":false}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":{}}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":[\"invalid\"]}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":[5]}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":5}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":\"document\"}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":null}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":false}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
+
+ StringBuilder rules;
+ rules.append("[");
+ for (unsigned i = 0; i < 49999; ++i)
+ rules.append("{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"a\"}},");
+ String rules50000 = rules.toString();
+ String rules50001 = rules.toString();
+ rules50000.append("{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"a\"}}]");
+ rules50001.append("{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"a\"}},{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"a\"}}]");
+ checkCompilerError(rules50000.utf8().data(), { });
+ checkCompilerError(rules50001.utf8().data(), ContentExtensions::ContentExtensionError::JSONTooManyRules);
+
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":{}}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[5]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"]}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":\"a\"}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":false}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":null}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":{}}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[5]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"\"]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":\"a\"}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":null}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":false}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"A\"]}}]", ContentExtensions::ContentExtensionError::JSONDomainNotLowerCaseASCII);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"\\u00DC\"]}}]", ContentExtensions::ContentExtensionError::JSONDomainNotLowerCaseASCII);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"0\"]}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"a\"]}}]", { });
+
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[],\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":5}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":5}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":5,\"unless-domain\":5}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"],\"unless-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONUnlessAndIfDomain);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"],\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONUnlessAndIfDomain);
+
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\", \"unexpected-identifier-should-be-ignored\":5}}]", { });
+
+ checkCompilerError("[{\"action\":5,\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidAction);
+ checkCompilerError("[{\"action\":{\"type\":\"invalid\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidActionType);
+ checkCompilerError("[{\"action\":{\"type\":\"css-display-none\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidCSSDisplayNoneActionType);
+
+ checkCompilerError("[{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"webkit.org\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"if-domain\":[\"a\"]}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"unless-domain\":[\"a\"]}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[\"}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidRegex);
+}
+
+TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines1)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^.*foo\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar$\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[ab]+bang\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("foo://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bar://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("abang://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bbang://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("cbang://webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/bang"), { });
+ testRequest(backend, mainDocumentRequest("bang://webkit.org/"), { });
+}
+
+TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines1Partitioning)
+{
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+
+ // Those two share a prefix.
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^.*foo", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("bar$", false, 1));
+
+ // Not this one.
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^[ab]+bang", false, 0));
+
+ EXPECT_EQ(2ul, createNFAs(combinedURLFilters).size());
+}
+
+TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines2)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^foo\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^.*[a-c]+bar\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^webkit:\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-c]+b+oom\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("foo://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("webkit://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("http://bar.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://abar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://bbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://cbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://abcbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://dbar.org/"), { });
+}
+
+TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines2Partitioning)
+{
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^foo", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^.*[a-c]+bar", false, 1));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^webkit:", false, 2));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("[a-c]+b+oom", false, 3));
+
+ // "^foo" and "^webkit:" can be grouped, the other two have a variable prefix.
+ EXPECT_EQ(3ul, createNFAs(combinedURLFilters).size());
+}
+
+TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines3)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"A*D\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"A*BA+\"}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"A*BC\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/D"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/AAD"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/AB"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ABA"), { }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ABAD"), { }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/BC"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ABC"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ABABC"), { ContentExtensions::ActionType::BlockCookies }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ABABCAD"), { ContentExtensions::ActionType::BlockCookies }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ABCAD"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
+}
+
+TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines3Partitioning)
+{
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A*D", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A*BA+", false, 1));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A*BC", false, 2));
+
+ // "A*A" and "A*BC" can be grouped, "A*BA+" should not.
+ EXPECT_EQ(2ul, createNFAs(combinedURLFilters).size());
+}
+
+TEST_F(ContentExtensionTest, SplittingLargeNFAs)
+{
+ const size_t expectedNFACounts[16] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1};
+
+ for (size_t i = 0; i < 16; i++) {
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A+BBB", false, 1));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A+CCC", false, 2));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A+DDD", false, 2));
+
+ Vector<ContentExtensions::NFA> nfas;
+ combinedURLFilters.processNFAs(i, [&](ContentExtensions::NFA&& nfa) {
+ nfas.append(WTF::move(nfa));
+ });
+ EXPECT_EQ(nfas.size(), expectedNFACounts[i]);
+
+ Vector<ContentExtensions::DFA> dfas;
+ for (auto& nfa : nfas)
+ dfas.append(ContentExtensions::NFAToDFA::convert(nfa));
+
+ Vector<ContentExtensions::DFABytecode> combinedBytecode;
+ for (const auto& dfa : dfas) {
+ Vector<ContentExtensions::DFABytecode> bytecode;
+ ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode);
+ compiler.compile();
+ combinedBytecode.appendVector(bytecode);
+ }
+
+ ContentExtensions::DFABytecodeInterpreter interpreter(&combinedBytecode[0], combinedBytecode.size());
+
+ EXPECT_EQ(interpreter.interpret("ABBBX", 0).size(), 1ull);
+ EXPECT_EQ(interpreter.interpret("ACCCX", 0).size(), 1ull);
+ EXPECT_EQ(interpreter.interpret("ADDDX", 0).size(), 1ull);
+ EXPECT_EQ(interpreter.interpret("XBBBX", 0).size(), 0ull);
+ EXPECT_EQ(interpreter.interpret("ABBX", 0).size(), 0ull);
+ EXPECT_EQ(interpreter.interpret("ACCX", 0).size(), 0ull);
+ EXPECT_EQ(interpreter.interpret("ADDX", 0).size(), 0ull);
+ }
+}
+
+TEST_F(ContentExtensionTest, QuantifierInGroup)
+{
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(((A+)B)C)", false, 0));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(((A)B+)C)", false, 1));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(((A)B+)C)D", false, 2));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(((A)B)C+)", false, 3));
+ EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(((A)B)C)", false, 4));
+
+ // (((A)B+)C) and (((A)B+)C)D should be in the same NFA.
+ EXPECT_EQ(4ul, createNFAs(combinedURLFilters).size());
+}
+
+static void testPatternStatus(String pattern, ContentExtensions::URLFilterParser::ParseStatus status)
+{
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+ EXPECT_EQ(status, parser.addPattern(pattern, false, 0));
+}
+
+TEST_F(ContentExtensionTest, ParsingFailures)
+{
+ testPatternStatus("a*b?.*.?[a-z]?[a-z]*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("a*b?.*.?[a-z]?[a-z]+", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("a*b?.*.?[a-z]?[a-z]", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus(".*?a", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus(".*a", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+
+ testPatternStatus("(?!)", ContentExtensions::URLFilterParser::ParseStatus::Group);
+ testPatternStatus("(?=)", ContentExtensions::URLFilterParser::ParseStatus::Group);
+ testPatternStatus("(?!a)", ContentExtensions::URLFilterParser::ParseStatus::Group);
+ testPatternStatus("(?=a)", ContentExtensions::URLFilterParser::ParseStatus::Group);
+ testPatternStatus("(regex)", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("(regex", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
+ testPatternStatus("((regex)", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
+ testPatternStatus("(?:regex)", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("(?:regex", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
+ testPatternStatus("[^.]+", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+
+ testPatternStatus("a++", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
+ testPatternStatus("[a]++", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
+ testPatternStatus("+", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
+
+ testPatternStatus("[", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
+ testPatternStatus("[a}", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
+
+ // FIXME: Look into why these do not cause YARR parsing errors. They probably should.
+ testPatternStatus("a]", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("{", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("{[a]", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("{0", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("{0,", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("{0,1", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("a{0,1", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("a{a,b}", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+
+ const char nonASCII[2] = {-1, '\0'};
+ testPatternStatus(nonASCII, ContentExtensions::URLFilterParser::ParseStatus::NonASCII);
+ testPatternStatus("\\xff", ContentExtensions::URLFilterParser::ParseStatus::NonASCII);
+
+ testPatternStatus("\\x\\r\\n", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("\\b", ContentExtensions::URLFilterParser::ParseStatus::WordBoundary);
+ testPatternStatus("[\\d]", ContentExtensions::URLFilterParser::ParseStatus::AtomCharacter);
+ testPatternStatus("\\d\\D\\w\\s\\v\\h\\i\\c", ContentExtensions::URLFilterParser::ParseStatus::UnsupportedCharacterClass);
+
+ testPatternStatus("this|that", ContentExtensions::URLFilterParser::ParseStatus::Disjunction);
+ testPatternStatus("a{0,1}b", ContentExtensions::URLFilterParser::ParseStatus::Ok);
+ testPatternStatus("a{0,2}b", ContentExtensions::URLFilterParser::ParseStatus::InvalidQuantifier);
+ testPatternStatus("", ContentExtensions::URLFilterParser::ParseStatus::EmptyPattern);
+ testPatternStatus("$$", ContentExtensions::URLFilterParser::ParseStatus::MisplacedEndOfLine);
+ testPatternStatus("a^", ContentExtensions::URLFilterParser::ParseStatus::MisplacedStartOfLine);
+ testPatternStatus("(^)", ContentExtensions::URLFilterParser::ParseStatus::MisplacedStartOfLine);
+
+ testPatternStatus("(a)\\1", ContentExtensions::URLFilterParser::ParseStatus::Ok); // This should be BackReference, right?
+}
+
+TEST_F(ContentExtensionTest, PatternMatchingTheEmptyString)
+{
+ // Simple atoms.
+ testPatternStatus(".*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("a*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus(".?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("a?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+
+ // Character sets.
+ testPatternStatus("[a-z]*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("[a-z]?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+
+ // Groups.
+ testPatternStatus("(foobar)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("(foobar)?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("(.*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("(a*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("(.?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("(a?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("([a-z]*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("([a-z]?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+
+ testPatternStatus("(.)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("(.+)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("(.?)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("(.*)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("(.+)?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+ testPatternStatus("(.?)+", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+
+ // Nested groups.
+ testPatternStatus("((foo)?((.)*)(bar)*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
+}
+
+TEST_F(ContentExtensionTest, MinimizingWithMoreFinalStatesThanNonFinalStates)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^h[a-z://]+\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://foo.com/\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://bar.com/\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://foo.com/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://bar.com/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("attp://foo.com/"), { });
+ testRequest(backend, mainDocumentRequest("attp://bar.com/"), { });
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bttp://webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("bttps://webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/b"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://webkit.org/b"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("cttp://webkit.org/B"), { });
+ testRequest(backend, mainDocumentRequest("cttps://webkit.org/B"), { });
+}
+
+TEST_F(ContentExtensionTest, StatesWithDifferentActionsAreNotUnified1)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://www.webkit.org/\"}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"^https://www.webkit.org/\"}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"^attps://www.webkit.org/\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://www.webkit.org/"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("attps://www.webkit.org/"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://www.webkit.org/a"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://www.webkit.org/B"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("attps://www.webkit.org/c"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("http://www.whatwg.org/"), { });
+ testRequest(backend, mainDocumentRequest("https://www.whatwg.org/"), { });
+ testRequest(backend, mainDocumentRequest("attps://www.whatwg.org/"), { });
+}
+
+TEST_F(ContentExtensionTest, StatesWithDifferentActionsAreNotUnified2)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://www.webkit.org/\"}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"^https://www.webkit.org/\"}},"
+ "{\"action\":{\"type\":\"css-display-none\", \"selector\":\"#foo\"},\"trigger\":{\"url-filter\":\"^https://www.webkit.org/\"}}]");
+
+ testRequest(backend, mainDocumentRequest("http://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("https://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
+ testRequest(backend, mainDocumentRequest("https://www.whatwg.org/"), { });
+ testRequest(backend, mainDocumentRequest("attps://www.whatwg.org/"), { });
+}
+
+// The order in which transitions from the root will be processed is unpredictable.
+// To exercises the various options, this test exists in various version exchanging the transition to the final state.
+TEST_F(ContentExtensionTest, FallbackTransitionsWithDifferentiatorDoNotMerge1)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.a\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^b.a\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bac\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bbc\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^BCC\"}}]");
+
+ testRequest(backend, mainDocumentRequest("aza://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bac://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bbc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bcc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("aac://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("abc://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("acc://www.webkit.org/"), { });
+
+ testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { });
+}
+TEST_F(ContentExtensionTest, FallbackTransitionsWithDifferentiatorDoNotMerge2)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bac\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bbc\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^BCC\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.a\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^b.a\"}}]");
+
+ testRequest(backend, mainDocumentRequest("aza://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bac://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bbc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bcc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("aac://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("abc://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("acc://www.webkit.org/"), { });
+
+ testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { });
+}
+TEST_F(ContentExtensionTest, FallbackTransitionsWithDifferentiatorDoNotMerge3)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.c\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^b.c\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^baa\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bba\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^BCA\"}}]");
+
+ testRequest(backend, mainDocumentRequest("azc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("baa://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bba://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bca://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("aaa://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("aba://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("aca://www.webkit.org/"), { });
+
+ testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { });
+}
+TEST_F(ContentExtensionTest, FallbackTransitionsWithDifferentiatorDoNotMerge4)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^baa\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bba\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^BCA\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.c\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^b.c\"}}]");
+
+ testRequest(backend, mainDocumentRequest("azc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("baa://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bba://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bca://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("aaa://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("aba://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("aca://www.webkit.org/"), { });
+
+ testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { });
+}
+
+TEST_F(ContentExtensionTest, FallbackTransitionsToOtherNodeInSameGroupDoesNotDifferentiateGroup)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^aac\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.c\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^b.c\"}}]");
+
+ testRequest(backend, mainDocumentRequest("aac://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("abc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("bac://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("abc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("aaa://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("aca://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("baa://www.webkit.org/"), { });
+}
+
+TEST_F(ContentExtensionTest, SimpleFallBackTransitionDifferentiator1)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.bc.de\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.bd.ef\"}}]");
+
+ testRequest(backend, mainDocumentRequest("abbccde://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("aabcdde://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("aabddef://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("aabddef://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("abcde://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("abdef://www.webkit.org/"), { });
+}
+
+TEST_F(ContentExtensionTest, SimpleFallBackTransitionDifferentiator2)
+{
+ auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^cb.\"}},"
+ "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^db.b\"}}]");
+
+ testRequest(backend, mainDocumentRequest("cba://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("cbb://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("dbab://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("dbxb://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+ testRequest(backend, mainDocumentRequest("cca://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("dddd://www.webkit.org/"), { });
+ testRequest(backend, mainDocumentRequest("bbbb://www.webkit.org/"), { });
+}
+
+// *** We have the following ranges intersections: ***
+// Full overlap.
+// 1)
+// A: |-----|
+// B: |---|
+// 2)
+// A: |-----|
+// B: |
+// 3)
+// A: |---|
+// B: |-----|
+// 4)
+// A: |
+// B: |-----|
+// One edge in common
+// 5)
+// A: |-|
+// B: |-|
+// 6)
+// A: |
+// B: |-|
+// 7)
+// A: |-|
+// B: |
+// 8)
+// A: |-|
+// B: |-|
+// 9)
+// A: |
+// B: |-|
+// 10)
+// A: |-|
+// B: |
+// B overlap on the left side of A.
+// 11)
+// A: |---|
+// B: |---|
+// 12)
+// A: |---|
+// B: |-----|
+// A overlap on the left side of B
+// 13)
+// A: |---|
+// B: |---|
+// 14)
+// A: |-----|
+// B: |---|
+// Equal ranges
+// 15)
+// A: |---|
+// B: |---|
+// 16)
+// A: |
+// B: |
+
+TEST_F(ContentExtensionTest, RangeOverlapCase1)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[a-m]\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"^[c-e]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { }, true);
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { }, true);
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { }, true);
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-m]\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"[c-e]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { }, true);
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { }, true);
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { }, true);
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase2)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[a-m]\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"^b\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { }, true);
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-m]\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"l\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.k.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.l.xxx/"), { }, true);
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase3)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[b-d]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[a-m]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[b-d]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[a-m]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase4)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^l\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[a-m]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("k://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("l://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"l\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[a-m]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.k.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.l.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase5)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[a-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[e-h]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[e-h]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase6)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^e\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[e-h]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"e\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[e-h]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase7)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[a-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^e\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"e\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase8)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[e-h]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[a-e]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[e-h]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[a-e]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase9)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^e\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[a-e]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"e\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[a-e]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase10)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[e-h]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^e\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[e-h]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"e\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase11)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[e-g]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[d-f]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[e-g]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[d-f]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { });
+}
+
+
+TEST_F(ContentExtensionTest, RangeOverlapCase12)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[e-g]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[d-g]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[e-g]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[d-g]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase13)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[b-d]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[c-e]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[b-d]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[c-e]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase14)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[b-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[c-e]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[b-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[c-e]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase15)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[c-f]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[c-f]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[c-f]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[c-f]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, RangeOverlapCase16)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^c\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^c\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"c\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"c\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, QuantifiedOneOrMoreRangesCase11And13)
+{
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[c-e]+[g-i]+YYY\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[b-d]+[h-j]+YYY\"}}]");
+
+ // The interesting ranges only match between 'b' and 'k', plus a gap in 'f'.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.abyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.acyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.adyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aeyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.afyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.agyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ahyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aiyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ajyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.akyyy.xxx/"), { });
+
+ // 'b' is the first character of the second rule.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.byyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bbyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bcyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bdyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.beyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bfyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bgyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.biyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bkyyy.xxx/"), { });
+
+ // 'c' is the first character of the first rule, and it overlaps the first character of the second rule.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cbyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ccyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cdyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ceyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cfyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.chyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ciyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ckyyy.xxx/"), { });
+
+ // 'd' is in the first range of both rule. This series cover overlaps between the two rules.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.degyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dehyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dfgyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dfhyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.djyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+}
+
+TEST_F(ContentExtensionTest, QuantifiedOneOrMoreRangesCase11And13InGroups)
+{
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"([c-e])+([g-i]+YYY)\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[b-d]+[h-j]+YYY\"}}]");
+
+ // The interesting ranges only match between 'b' and 'k', plus a gap in 'f'.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.abyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.acyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.adyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aeyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.afyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.agyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ahyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aiyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ajyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.akyyy.xxx/"), { });
+
+ // 'b' is the first character of the second rule.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.byyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bbyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bcyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bdyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.beyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bfyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bgyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.biyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bkyyy.xxx/"), { });
+
+ // 'c' is the first character of the first rule, and it overlaps the first character of the second rule.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cbyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ccyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cdyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ceyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cfyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.chyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ciyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ckyyy.xxx/"), { });
+
+ // 'd' is in the first range of both rule. This series cover overlaps between the two rules.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.degyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dehyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dfgyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dfhyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.djyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase1)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[a-m]\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"^(bar)*[c-e]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { }, true);
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { }, true);
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { }, true);
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[a-m]\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"(bar)*[c-e]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { }, true);
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { }, true);
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { }, true);
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { });
+}
+
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase2)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[a-m]\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"^(bar)*b\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { }, true);
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[a-m]\"}},"
+ "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"(bar)*l\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.k.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.l.xxx/"), { }, true);
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase3)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[b-d]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[a-m]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[b-d]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[a-m]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase4)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*l\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[a-m]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("k://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("l://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*l\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[a-m]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.k.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.l.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase5)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[a-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[e-h]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[a-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[e-h]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase6)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*e\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[e-h]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*e\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[e-h]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase7)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[a-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*e\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[a-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*e\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase8)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[e-h]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[a-e]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[e-h]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[a-e]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase9)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*e\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[a-e]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*e\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[a-e]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase10)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[e-h]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*e\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[e-h]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*e\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase11)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[e-g]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[d-f]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[e-g]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[d-f]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { });
+}
+
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase12)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[e-g]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[d-g]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[e-g]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[d-g]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase13)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[b-d]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[c-e]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[b-d]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[c-e]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase14)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[b-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[c-e]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[b-e]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[c-e]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase15)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[c-f]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[c-f]\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[c-f]\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[c-f]\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase16)
+{
+ auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*c\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*c\"}}]");
+
+ testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { });
+ testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { });
+
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*c\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*c\"}}]");
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedQuantifiedOneOrMoreRangesCase11And13)
+{
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[c-e]+[g-i]+YYY\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[b-d]+[h-j]+YYY\"}}]");
+
+ // The interesting ranges only match between 'b' and 'k', plus a gap in 'f'.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.abyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.acyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.adyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aeyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.afyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.agyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ahyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aiyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ajyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.akyyy.xxx/"), { });
+
+ // 'b' is the first character of the second rule.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.byyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bbyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bcyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bdyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.beyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bfyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bgyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.biyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bkyyy.xxx/"), { });
+
+ // 'c' is the first character of the first rule, and it overlaps the first character of the second rule.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cbyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ccyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cdyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ceyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cfyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.chyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ciyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ckyyy.xxx/"), { });
+
+ // 'd' is in the first range of both rule. This series cover overlaps between the two rules.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.degyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dehyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dfgyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dfhyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.djyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+}
+
+TEST_F(ContentExtensionTest, CombinedQuantifiedOneOrMoreRangesCase11And13InGroups)
+{
+ auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*([c-e])+([g-i]+YYY)\"}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[b-d]+[h-j]+YYY\"}}]");
+
+ // The interesting ranges only match between 'b' and 'k', plus a gap in 'f'.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.abyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.acyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.adyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aeyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.afyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.agyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ahyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.aiyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ajyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.akyyy.xxx/"), { });
+
+ // 'b' is the first character of the second rule.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.byyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bbyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bcyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bdyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.beyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bfyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bgyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.biyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.bkyyy.xxx/"), { });
+
+ // 'c' is the first character of the first rule, and it overlaps the first character of the second rule.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cayyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cbyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ccyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cdyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ceyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cfyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.chyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ciyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.cjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ckyyy.xxx/"), { });
+
+ // 'd' is in the first range of both rule. This series cover overlaps between the two rules.
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.degyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dehyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dfgyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.dfhyyy.xxx/"), { });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.djyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp b/Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp
new file mode 100644
index 000000000..30bc85997
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "DFAHelpers.h"
+#include <WebCore/DFACombiner.h>
+#include <wtf/MainThread.h>
+
+using namespace WebCore;
+using namespace ContentExtensions;
+
+namespace TestWebKitAPI {
+
+class DFACombinerTest : public testing::Test {
+public:
+ virtual void SetUp()
+ {
+ WTF::initializeMainThread();
+ }
+};
+
+Vector<DFA> combine(Vector<DFA> dfas, unsigned minimumSize)
+{
+ DFACombiner combiner;
+ for (DFA& dfa : dfas)
+ combiner.addDFA(WTF::move(dfa));
+
+ Vector<DFA> output;
+ combiner.combineDFAs(minimumSize, [&output](DFA&& dfa) {
+ output.append(dfa);
+ });
+ return output;
+}
+
+TEST_F(DFACombinerTest, Basic)
+{
+ Vector<DFA> dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "bar"}) };
+ Vector<DFA> combinedDFAs = combine(dfas, 10000);
+ EXPECT_EQ(static_cast<size_t>(1), combinedDFAs.size());
+
+ DFA reference = buildDFAFromPatterns({ "foo", "bar"});
+ reference.minimize();
+ EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first()));
+}
+
+
+TEST_F(DFACombinerTest, IdenticalDFAs)
+{
+ Vector<DFA> dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "foo"}) };
+ Vector<DFA> combinedDFAs = combine(dfas, 10000);
+ EXPECT_EQ(static_cast<size_t>(1), combinedDFAs.size());
+
+ // The result should have the exact same size as the minimized input.
+ DFA reference = buildDFAFromPatterns({ "foo"});
+ reference.minimize();
+ EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first()));
+}
+
+TEST_F(DFACombinerTest, NoInput)
+{
+ DFACombiner combiner;
+ unsigned counter = 0;
+ combiner.combineDFAs(100000, [&counter](DFA&& dfa) {
+ ++counter;
+ });
+ EXPECT_EQ(static_cast<unsigned>(0), counter);
+}
+
+TEST_F(DFACombinerTest, SingleInput)
+{
+ Vector<DFA> dfas = { buildDFAFromPatterns({ "WebKit"}) };
+ Vector<DFA> combinedDFAs = combine(dfas, 10000);
+ EXPECT_EQ(static_cast<size_t>(1), combinedDFAs.size());
+
+ DFA reference = buildDFAFromPatterns({ "WebKit"});
+ reference.minimize();
+ EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first()));
+}
+
+TEST_F(DFACombinerTest, InputTooLargeForMinimumSize)
+{
+ Vector<DFA> dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "bar"}) };
+ Vector<DFA> combinedDFAs = combine(dfas, 2);
+ EXPECT_EQ(static_cast<size_t>(2), combinedDFAs.size());
+ EXPECT_EQ(static_cast<size_t>(4), countLiveNodes(combinedDFAs[0]));
+ EXPECT_EQ(static_cast<size_t>(4), countLiveNodes(combinedDFAs[1]));
+}
+
+TEST_F(DFACombinerTest, CombinedInputReachesMinimumSize)
+{
+ Vector<DFA> dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "bar"}), buildDFAFromPatterns({ "WebKit"}) };
+ Vector<DFA> combinedDFAs = combine(dfas, 5);
+ EXPECT_EQ(static_cast<size_t>(2), combinedDFAs.size());
+ EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(combinedDFAs[0]));
+ EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(combinedDFAs[1]));
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h b/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h
new file mode 100644
index 000000000..432af23ca
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <WebCore/CombinedURLFilters.h>
+#include <WebCore/NFA.h>
+#include <WebCore/NFAToDFA.h>
+#include <WebCore/URLFilterParser.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+static unsigned countLiveNodes(const ContentExtensions::DFA& dfa)
+{
+ unsigned counter = 0;
+ for (const auto& node : dfa.nodes) {
+ if (!node.isKilled())
+ ++counter;
+ }
+ return counter;
+}
+
+static Vector<ContentExtensions::NFA> createNFAs(ContentExtensions::CombinedURLFilters& combinedURLFilters)
+{
+ Vector<ContentExtensions::NFA> nfas;
+
+ combinedURLFilters.processNFAs(std::numeric_limits<size_t>::max(), [&](ContentExtensions::NFA&& nfa) {
+ nfas.append(WTF::move(nfa));
+ });
+
+ return nfas;
+}
+
+static ContentExtensions::DFA buildDFAFromPatterns(Vector<const char*> patterns)
+{
+ ContentExtensions::CombinedURLFilters combinedURLFilters;
+ ContentExtensions::URLFilterParser parser(combinedURLFilters);
+
+ for (const char* pattern : patterns)
+ parser.addPattern(pattern, false, 0);
+ Vector<ContentExtensions::NFA> nfas = createNFAs(combinedURLFilters);
+ return ContentExtensions::NFAToDFA::convert(nfas[0]);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp b/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp
new file mode 100644
index 000000000..bb3ac2e2a
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "DFAHelpers.h"
+#include <wtf/MainThread.h>
+
+namespace TestWebKitAPI {
+
+class DFAMinimizerTest : public testing::Test {
+public:
+ virtual void SetUp()
+ {
+ WTF::initializeMainThread();
+ }
+};
+
+TEST_F(DFAMinimizerTest, BasicSearch)
+{
+ ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*foo", ".*bar", ".*bang"});
+ EXPECT_EQ(static_cast<size_t>(8), countLiveNodes(dfa));
+ dfa.minimize();
+ EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa));
+}
+
+TEST_F(DFAMinimizerTest, MergeSuffixes)
+{
+ ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*aaa", ".*aab", ".*aba", ".*abb", ".*baa", ".*bab", ".*bba", ".*bbb"});
+ EXPECT_EQ(static_cast<size_t>(12), countLiveNodes(dfa));
+ dfa.minimize();
+ EXPECT_EQ(static_cast<size_t>(4), countLiveNodes(dfa));
+}
+
+TEST_F(DFAMinimizerTest, MergeInfixes)
+{
+ ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*aaakit", ".*aabkit", ".*abakit", ".*abbkit", ".*baakit", ".*babkit", ".*bbakit", ".*bbbkit"});
+ EXPECT_EQ(static_cast<size_t>(15), countLiveNodes(dfa));
+ dfa.minimize();
+ EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa));
+}
+
+TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge1)
+{
+ ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.a", "^b.a", "^bac", "^bbc", "^BCC"});
+ EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
+ dfa.minimize();
+ EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
+}
+
+TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge2)
+{
+ ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^bbc", "^BCC", "^a.a", "^b.a"});
+ EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
+ dfa.minimize();
+ EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
+}
+
+TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge3)
+{
+ ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.c", "^b.c", "^baa", "^bba", "^BCA"});
+ EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
+ dfa.minimize();
+ EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
+}
+
+TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge4)
+{
+ ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^baa", "^bba", "^BCA", "^a.c", "^b.c"});
+ EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
+ dfa.minimize();
+ EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
+}
+
+TEST_F(DFAMinimizerTest, FallbackTransitionsToOtherNodeInSameGroupDoesNotDifferentiateGroup)
+{
+ ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^aac", "^a.c", "^b.c"});
+ EXPECT_EQ(static_cast<size_t>(5), countLiveNodes(dfa));
+ dfa.minimize();
+ EXPECT_EQ(static_cast<size_t>(4), countLiveNodes(dfa));
+}
+
+TEST_F(DFAMinimizerTest, SimpleFallBackTransitionDifferentiator1)
+{
+ ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.bc.de", "^a.bd.ef"});
+ EXPECT_EQ(static_cast<size_t>(11), countLiveNodes(dfa));
+ dfa.minimize();
+ EXPECT_EQ(static_cast<size_t>(11), countLiveNodes(dfa));
+}
+
+TEST_F(DFAMinimizerTest, SimpleFallBackTransitionDifferentiator2)
+{
+ ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^cb.", "^db.b"});
+ EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa));
+ dfa.minimize();
+ EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa));
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/FileSystem.cpp b/Tools/TestWebKitAPI/Tests/WebCore/FileSystem.cpp
new file mode 100644
index 000000000..fb23a4b7c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/FileSystem.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 Canon Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "Test.h"
+#include <WebCore/FileSystem.h>
+#include <wtf/MainThread.h>
+#include <wtf/StringExtras.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+const char* FileSystemTestData = "This is a test";
+
+// FIXME: Refactor FileSystemTest and SharedBufferTest as a single class.
+class FileSystemTest : public testing::Test {
+public:
+ virtual void SetUp() override
+ {
+ WTF::initializeMainThread();
+
+ // create temp file
+ PlatformFileHandle handle;
+ m_tempFilePath = openTemporaryFile("tempTestFile", handle);
+ writeToFile(handle, FileSystemTestData, strlen(FileSystemTestData));
+ closeFile(handle);
+
+ m_tempEmptyFilePath = openTemporaryFile("tempEmptyTestFile", handle);
+ closeFile(handle);
+ }
+
+ virtual void TearDown() override
+ {
+ deleteFile(m_tempFilePath);
+ deleteFile(m_tempEmptyFilePath);
+ }
+
+ const String& tempFilePath() { return m_tempFilePath; }
+ const String& tempEmptyFilePath() { return m_tempEmptyFilePath; }
+
+private:
+ String m_tempFilePath;
+ String m_tempEmptyFilePath;
+};
+
+TEST_F(FileSystemTest, MappingMissingFile)
+{
+ bool success;
+ MappedFileData mappedFileData(String("not_existing_file"), success);
+ EXPECT_FALSE(success);
+ EXPECT_TRUE(!mappedFileData);
+}
+
+TEST_F(FileSystemTest, MappingExistingFile)
+{
+ bool success;
+ MappedFileData mappedFileData(tempFilePath(), success);
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(!!mappedFileData);
+ EXPECT_TRUE(mappedFileData.size() == strlen(FileSystemTestData));
+ EXPECT_TRUE(strnstr(FileSystemTestData, static_cast<const char*>(mappedFileData.data()), mappedFileData.size()));
+}
+
+TEST_F(FileSystemTest, MappingExistingEmptyFile)
+{
+ bool success;
+ MappedFileData mappedFileData(tempEmptyFilePath(), success);
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(!mappedFileData);
+}
+
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/LayoutUnit.cpp b/Tools/TestWebKitAPI/Tests/WebCore/LayoutUnit.cpp
new file mode 100644
index 000000000..84e460f62
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/LayoutUnit.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2012, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define ENABLE_SATURATED_LAYOUT_ARITHMETIC 1
+#include "config.h"
+
+#include <WebCore/LayoutUnit.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+TEST(WebCoreLayoutUnit, LayoutUnitInt)
+{
+ ASSERT_EQ(LayoutUnit(INT_MIN).toInt(), intMinForLayoutUnit);
+ ASSERT_EQ(LayoutUnit(INT_MIN / 2).toInt(), intMinForLayoutUnit);
+ ASSERT_EQ(LayoutUnit(intMinForLayoutUnit - 1).toInt(), intMinForLayoutUnit);
+ ASSERT_EQ(LayoutUnit(intMinForLayoutUnit).toInt(), intMinForLayoutUnit);
+ ASSERT_EQ(LayoutUnit(intMinForLayoutUnit + 1).toInt(), intMinForLayoutUnit + 1);
+ ASSERT_EQ(LayoutUnit(intMinForLayoutUnit / 2).toInt(), intMinForLayoutUnit / 2);
+ ASSERT_EQ(LayoutUnit(-10000).toInt(), -10000);
+ ASSERT_EQ(LayoutUnit(-1000).toInt(), -1000);
+ ASSERT_EQ(LayoutUnit(-100).toInt(), -100);
+ ASSERT_EQ(LayoutUnit(-10).toInt(), -10);
+ ASSERT_EQ(LayoutUnit(-1).toInt(), -1);
+ ASSERT_EQ(LayoutUnit(0).toInt(), 0);
+ ASSERT_EQ(LayoutUnit(1).toInt(), 1);
+ ASSERT_EQ(LayoutUnit(100).toInt(), 100);
+ ASSERT_EQ(LayoutUnit(1000).toInt(), 1000);
+ ASSERT_EQ(LayoutUnit(10000).toInt(), 10000);
+ ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit / 2).toInt(), intMaxForLayoutUnit / 2);
+ ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit - 1).toInt(), intMaxForLayoutUnit - 1);
+ ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit).toInt(), intMaxForLayoutUnit);
+ ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit + 1).toInt(), intMaxForLayoutUnit);
+ ASSERT_EQ(LayoutUnit(INT_MAX / 2).toInt(), intMaxForLayoutUnit);
+ ASSERT_EQ(LayoutUnit(INT_MAX).toInt(), intMaxForLayoutUnit);
+}
+
+TEST(WebCoreLayoutUnit, LayoutUnitFloat)
+{
+ const float tolerance = 1.0f / kFixedPointDenominator;
+ ASSERT_FLOAT_EQ(LayoutUnit(1.0f).toFloat(), 1.0f);
+ ASSERT_FLOAT_EQ(LayoutUnit(1.25f).toFloat(), 1.25f);
+ ASSERT_NEAR(LayoutUnit(1.1f).toFloat(), 1.1f, tolerance);
+ ASSERT_NEAR(LayoutUnit(1.33f).toFloat(), 1.33f, tolerance);
+ ASSERT_NEAR(LayoutUnit(1.3333f).toFloat(), 1.3333f, tolerance);
+ ASSERT_NEAR(LayoutUnit(1.53434f).toFloat(), 1.53434f, tolerance);
+ ASSERT_NEAR(LayoutUnit(345634).toFloat(), 345634.0f, tolerance);
+ ASSERT_NEAR(LayoutUnit(345634.12335f).toFloat(), 345634.12335f, tolerance);
+ ASSERT_NEAR(LayoutUnit(-345634.12335f).toFloat(), -345634.12335f, tolerance);
+ ASSERT_NEAR(LayoutUnit(-345634).toFloat(), -345634.0f, tolerance);
+ ASSERT_NEAR(LayoutUnit(33554432.f).toFloat(), 33554432.f, tolerance);
+ ASSERT_NEAR(LayoutUnit(-33554432.f).toFloat(), -33554432.f, tolerance);
+ ASSERT_NEAR(LayoutUnit(33554432.f).toDouble(), 33554432.f, tolerance);
+ ASSERT_NEAR(LayoutUnit(-33554432.f).toDouble(), -33554432.f, tolerance);
+}
+
+TEST(WebCoreLayoutUnit, LayoutUnitRounding)
+{
+ ASSERT_EQ(LayoutUnit(-1.9f).round(), -2);
+ ASSERT_EQ(LayoutUnit(-1.6f).round(), -2);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(-1.51f).round(), -2);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(-1.5f).round(), -1);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(-1.49f).round(), -1);
+ ASSERT_EQ(LayoutUnit(-1.0f).round(), -1);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(-0.99f).round(), -1);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(-0.51f).round(), -1);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(-0.50f).round(), 0);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(-0.49f).round(), 0);
+ ASSERT_EQ(LayoutUnit(-0.1f).round(), 0);
+ ASSERT_EQ(LayoutUnit(0.0f).round(), 0);
+ ASSERT_EQ(LayoutUnit(0.1f).round(), 0);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(0.49f).round(), 0);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(0.50f).round(), 1);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(0.51f).round(), 1);
+ ASSERT_EQ(LayoutUnit(0.99f).round(), 1);
+ ASSERT_EQ(LayoutUnit(1.0f).round(), 1);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(1.49f).round(), 1);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(1.5f).round(), 2);
+ ASSERT_EQ(LayoutUnit::fromFloatRound(1.51f).round(), 2);
+}
+
+TEST(WebCoreLayoutUnit, LayoutUnitMultiplication)
+{
+ ASSERT_EQ((LayoutUnit(1) * LayoutUnit(1)).toInt(), 1);
+ ASSERT_EQ((LayoutUnit(1) * LayoutUnit(2)).toInt(), 2);
+ ASSERT_EQ((LayoutUnit(2) * LayoutUnit(1)).toInt(), 2);
+ ASSERT_EQ((LayoutUnit(2) * LayoutUnit(0.5)).toInt(), 1);
+ ASSERT_EQ((LayoutUnit(0.5) * LayoutUnit(2)).toInt(), 1);
+ ASSERT_EQ((LayoutUnit(100) * LayoutUnit(1)).toInt(), 100);
+
+ ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(1)).toInt(), -1);
+ ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(2)).toInt(), -2);
+ ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(1)).toInt(), -2);
+ ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(0.5)).toInt(), -1);
+ ASSERT_EQ((LayoutUnit(-0.5) * LayoutUnit(2)).toInt(), -1);
+ ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(1)).toInt(), -100);
+
+ ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(-1)).toInt(), 1);
+ ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(-2)).toInt(), 2);
+ ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(-1)).toInt(), 2);
+ ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(-0.5)).toInt(), 1);
+ ASSERT_EQ((LayoutUnit(-0.5) * LayoutUnit(-2)).toInt(), 1);
+ ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(-1)).toInt(), 100);
+
+ ASSERT_EQ((LayoutUnit(100) * LayoutUnit(3.33)).round(), 333);
+ ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(3.33)).round(), -333);
+ ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(-3.33)).round(), 333);
+
+ size_t aHundredSizeT = 100;
+ ASSERT_EQ((LayoutUnit(aHundredSizeT) * LayoutUnit(1)).toInt(), 100);
+ ASSERT_EQ((aHundredSizeT * LayoutUnit(4)).toInt(), 400);
+ ASSERT_EQ((LayoutUnit(4) * aHundredSizeT).toInt(), 400);
+
+ int quarterMax = intMaxForLayoutUnit / 4;
+ ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(2)).toInt(), quarterMax * 2);
+ ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(3)).toInt(), quarterMax * 3);
+ ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(4)).toInt(), quarterMax * 4);
+ ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(5)).toInt(), intMaxForLayoutUnit);
+
+ size_t overflowIntSizeT = intMaxForLayoutUnit * 4;
+ ASSERT_EQ((LayoutUnit(overflowIntSizeT) * LayoutUnit(2)).toInt(), intMaxForLayoutUnit);
+ ASSERT_EQ((overflowIntSizeT * LayoutUnit(4)).toInt(), intMaxForLayoutUnit);
+ ASSERT_EQ((LayoutUnit(4) * overflowIntSizeT).toInt(), intMaxForLayoutUnit);
+}
+
+TEST(WebCoreLayoutUnit, LayoutUnitDivision)
+{
+ ASSERT_EQ((LayoutUnit(1) / LayoutUnit(1)).toInt(), 1);
+ ASSERT_EQ((LayoutUnit(1) / LayoutUnit(2)).toInt(), 0);
+ ASSERT_EQ((LayoutUnit(2) / LayoutUnit(1)).toInt(), 2);
+ ASSERT_EQ((LayoutUnit(2) / LayoutUnit(0.5)).toInt(), 4);
+ ASSERT_EQ((LayoutUnit(0.5) / LayoutUnit(2)).toInt(), 0);
+ ASSERT_EQ((LayoutUnit(100) / LayoutUnit(10)).toInt(), 10);
+ ASSERT_FLOAT_EQ((LayoutUnit(1) / LayoutUnit(2)).toFloat(), 0.5f);
+ ASSERT_FLOAT_EQ((LayoutUnit(0.5) / LayoutUnit(2)).toFloat(), 0.25f);
+
+ ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(1)).toInt(), -1);
+ ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(2)).toInt(), 0);
+ ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(1)).toInt(), -2);
+ ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(0.5)).toInt(), -4);
+ ASSERT_EQ((LayoutUnit(-0.5) / LayoutUnit(2)).toInt(), 0);
+ ASSERT_EQ((LayoutUnit(-100) / LayoutUnit(10)).toInt(), -10);
+ ASSERT_FLOAT_EQ((LayoutUnit(-1) / LayoutUnit(2)).toFloat(), -0.5f);
+ ASSERT_FLOAT_EQ((LayoutUnit(-0.5) / LayoutUnit(2)).toFloat(), -0.25f);
+
+ ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(-1)).toInt(), 1);
+ ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(-2)).toInt(), 0);
+ ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(-1)).toInt(), 2);
+ ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(-0.5)).toInt(), 4);
+ ASSERT_EQ((LayoutUnit(-0.5) / LayoutUnit(-2)).toInt(), 0);
+ ASSERT_EQ((LayoutUnit(-100) / LayoutUnit(-10)).toInt(), 10);
+ ASSERT_FLOAT_EQ((LayoutUnit(-1) / LayoutUnit(-2)).toFloat(), 0.5f);
+ ASSERT_FLOAT_EQ((LayoutUnit(-0.5) / LayoutUnit(-2)).toFloat(), 0.25f);
+
+ size_t aHundredSizeT = 100;
+ ASSERT_EQ((LayoutUnit(aHundredSizeT) / LayoutUnit(2)).toInt(), 50);
+ ASSERT_EQ((aHundredSizeT / LayoutUnit(4)).toInt(), 25);
+ ASSERT_EQ((LayoutUnit(400) / aHundredSizeT).toInt(), 4);
+
+ ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) / LayoutUnit(2)).toInt(), intMaxForLayoutUnit / 2);
+ ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) / LayoutUnit(0.5)).toInt(), intMaxForLayoutUnit);
+}
+
+TEST(WebCoreLayoutUnit, LayoutUnitCeil)
+{
+ ASSERT_EQ(LayoutUnit(0).ceil(), 0);
+ ASSERT_EQ(LayoutUnit(0.1).ceil(), 1);
+ ASSERT_EQ(LayoutUnit(0.5).ceil(), 1);
+ ASSERT_EQ(LayoutUnit(0.9).ceil(), 1);
+ ASSERT_EQ(LayoutUnit(1.0).ceil(), 1);
+ ASSERT_EQ(LayoutUnit(1.1).ceil(), 2);
+
+ ASSERT_EQ(LayoutUnit(-0.1).ceil(), 0);
+ ASSERT_EQ(LayoutUnit(-0.5).ceil(), 0);
+ ASSERT_EQ(LayoutUnit(-0.9).ceil(), 0);
+ ASSERT_EQ(LayoutUnit(-1.0).ceil(), -1);
+
+ ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit).ceil(), intMaxForLayoutUnit);
+ ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) - LayoutUnit(0.5)).ceil(), intMaxForLayoutUnit);
+ ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) - LayoutUnit(1)).ceil(), intMaxForLayoutUnit - 1);
+
+ ASSERT_EQ(LayoutUnit(intMinForLayoutUnit).ceil(), intMinForLayoutUnit);
+}
+
+TEST(WebCoreLayoutUnit, LayoutUnitFloor)
+{
+ ASSERT_EQ(LayoutUnit(0).floor(), 0);
+ ASSERT_EQ(LayoutUnit(0.1).floor(), 0);
+ ASSERT_EQ(LayoutUnit(0.5).floor(), 0);
+ ASSERT_EQ(LayoutUnit(0.9).floor(), 0);
+ ASSERT_EQ(LayoutUnit(1.0).floor(), 1);
+ ASSERT_EQ(LayoutUnit(1.1).floor(), 1);
+
+ ASSERT_EQ(LayoutUnit(-0.1).floor(), -1);
+ ASSERT_EQ(LayoutUnit(-0.5).floor(), -1);
+ ASSERT_EQ(LayoutUnit(-0.9).floor(), -1);
+ ASSERT_EQ(LayoutUnit(-1.0).floor(), -1);
+
+ ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit).floor(), intMaxForLayoutUnit);
+
+ ASSERT_EQ(LayoutUnit(intMinForLayoutUnit).floor(), intMinForLayoutUnit);
+ ASSERT_EQ((LayoutUnit(intMinForLayoutUnit) + LayoutUnit(0.5)).floor(), intMinForLayoutUnit);
+ ASSERT_EQ((LayoutUnit(intMinForLayoutUnit) + LayoutUnit(1)).floor(), intMinForLayoutUnit + 1);
+}
+
+TEST(WebCoreLayoutUnit, LayoutUnitPixelSnapping)
+{
+ for (int i = -100000; i <= 100000; ++i) {
+ ASSERT_EQ(roundToDevicePixel(LayoutUnit(i), 1), i);
+ ASSERT_EQ(roundToDevicePixel(LayoutUnit(i), 2), i);
+ ASSERT_EQ(roundToDevicePixel(LayoutUnit(i), 3), i);
+ }
+
+ for (float i = -10000; i < 0; i = i + 0.5)
+ ASSERT_FLOAT_EQ(roundToDevicePixel(LayoutUnit(i), 2), i);
+
+ for (float i = -10000.25; i < 0; i = i + 0.5)
+ ASSERT_FLOAT_EQ(roundToDevicePixel(LayoutUnit(i), 2), i + 0.25);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/PublicSuffix.cpp b/Tools/TestWebKitAPI/Tests/WebCore/PublicSuffix.cpp
new file mode 100644
index 000000000..f87ba63eb
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/PublicSuffix.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(PUBLIC_SUFFIX_LIST)
+
+#include "WTFStringUtilities.h"
+#include <WebCore/PublicSuffix.h>
+#include <wtf/MainThread.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+class PublicSuffix: public testing::Test {
+public:
+ virtual void SetUp()
+ {
+ WTF::initializeMainThread();
+ }
+};
+
+TEST_F(PublicSuffix, IsPublicSuffix)
+{
+ EXPECT_TRUE(isPublicSuffix("com"));
+ EXPECT_FALSE(isPublicSuffix("test.com"));
+ EXPECT_FALSE(isPublicSuffix("com.com"));
+ EXPECT_TRUE(isPublicSuffix("net"));
+ EXPECT_TRUE(isPublicSuffix("org"));
+ EXPECT_TRUE(isPublicSuffix("co.uk"));
+ EXPECT_FALSE(isPublicSuffix("bl.uk"));
+ EXPECT_FALSE(isPublicSuffix("test.co.uk"));
+ EXPECT_TRUE(isPublicSuffix("xn--zf0ao64a.tw"));
+}
+
+TEST_F(PublicSuffix, TopPrivatelyControlledDomain)
+{
+ EXPECT_EQ(String("test.com"), topPrivatelyControlledDomain("test.com"));
+ EXPECT_EQ(String("test.com"), topPrivatelyControlledDomain("com.test.com"));
+ EXPECT_EQ(String("test.com"), topPrivatelyControlledDomain("subdomain.test.com"));
+ EXPECT_EQ(String("com.com"), topPrivatelyControlledDomain("www.com.com"));
+ EXPECT_EQ(String("test.co.uk"), topPrivatelyControlledDomain("test.co.uk"));
+ EXPECT_EQ(String("test.co.uk"), topPrivatelyControlledDomain("subdomain.test.co.uk"));
+ EXPECT_EQ(String("bl.uk"), topPrivatelyControlledDomain("bl.uk"));
+ EXPECT_EQ(String("bl.uk"), topPrivatelyControlledDomain("subdomain.bl.uk"));
+ EXPECT_EQ(String("test.xn--zf0ao64a.tw"), topPrivatelyControlledDomain("test.xn--zf0ao64a.tw"));
+ EXPECT_EQ(String("test.xn--zf0ao64a.tw"), topPrivatelyControlledDomain("www.test.xn--zf0ao64a.tw"));
+ EXPECT_EQ(String("127.0.0.1"), topPrivatelyControlledDomain("127.0.0.1"));
+ EXPECT_EQ(String(), topPrivatelyControlledDomain("1"));
+ EXPECT_EQ(String(), topPrivatelyControlledDomain("com"));
+}
+
+}
+
+#endif // ENABLE(PUBLIC_SUFFIX_LIST)
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/SharedBuffer.cpp b/Tools/TestWebKitAPI/Tests/WebCore/SharedBuffer.cpp
new file mode 100644
index 000000000..19e90b6d0
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/SharedBuffer.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 Canon Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "Test.h"
+#include <WebCore/SharedBuffer.h>
+#include <wtf/MainThread.h>
+#include <wtf/StringExtras.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+const char* SharedBufferTestData = "This is a test";
+
+class SharedBufferTest : public testing::Test {
+public:
+ virtual void SetUp() override
+ {
+ WTF::initializeMainThread();
+
+ // create temp file
+ PlatformFileHandle handle;
+ m_tempFilePath = openTemporaryFile("tempTestFile", handle);
+ writeToFile(handle, SharedBufferTestData, strlen(SharedBufferTestData));
+ closeFile(handle);
+
+ m_tempEmptyFilePath = openTemporaryFile("tempEmptyTestFile", handle);
+ closeFile(handle);
+ }
+
+ virtual void TearDown() override
+ {
+ deleteFile(m_tempFilePath);
+ deleteFile(m_tempEmptyFilePath);
+ }
+
+ const String& tempFilePath() { return m_tempFilePath; }
+ const String& tempEmptyFilePath() { return m_tempEmptyFilePath; }
+
+private:
+ String m_tempFilePath;
+ String m_tempEmptyFilePath;
+};
+
+TEST_F(SharedBufferTest, createWithContentsOfMissingFile)
+{
+ RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(String("not_existing_file"));
+ ASSERT_NULL(buffer);
+}
+
+TEST_F(SharedBufferTest, createWithContentsOfExistingFile)
+{
+ RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(tempFilePath());
+ ASSERT_NOT_NULL(buffer);
+ EXPECT_TRUE(buffer->size() == strlen(SharedBufferTestData));
+ EXPECT_TRUE(String(SharedBufferTestData) == String(buffer->data(), buffer->size()));
+}
+
+TEST_F(SharedBufferTest, createWithContentsOfExistingEmptyFile)
+{
+ RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(tempEmptyFilePath());
+ ASSERT_NOT_NULL(buffer);
+ EXPECT_TRUE(buffer->isEmpty());
+}
+
+TEST_F(SharedBufferTest, copyBufferCreatedWithContentsOfExistingFile)
+{
+ RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(tempFilePath());
+ ASSERT_NOT_NULL(buffer);
+ RefPtr<SharedBuffer> copy = buffer->copy();
+ EXPECT_TRUE(buffer->size() == copy->size());
+ EXPECT_TRUE(strnstr(buffer->data(), copy->data(), buffer->size()));
+}
+
+TEST_F(SharedBufferTest, clearBufferCreatedWithContentsOfExistingFile)
+{
+ RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(tempFilePath());
+ ASSERT_NOT_NULL(buffer);
+ buffer->clear();
+ EXPECT_TRUE(!buffer->size());
+ EXPECT_TRUE(!buffer->data());
+}
+
+
+TEST_F(SharedBufferTest, appendBufferCreatedWithContentsOfExistingFile)
+{
+ RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(tempFilePath());
+ ASSERT_NOT_NULL(buffer);
+ buffer->append("a", 1);
+ EXPECT_TRUE(buffer->size() == (strlen(SharedBufferTestData) + 1));
+ EXPECT_TRUE(strnstr(buffer->data(), SharedBufferTestData, strlen(SharedBufferTestData)));
+ EXPECT_EQ('a', buffer->data()[strlen(SharedBufferTestData)]);
+}
+
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/TimeRanges.cpp b/Tools/TestWebKitAPI/Tests/WebCore/TimeRanges.cpp
new file mode 100644
index 000000000..1612bf8f0
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/TimeRanges.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2013, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <WebCore/TimeRanges.h>
+
+#include <WebCore/ExceptionCodePlaceholder.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+static std::string ToString(const TimeRanges& ranges)
+{
+ std::stringstream ss;
+ ss << "{";
+ for (unsigned i = 0; i < ranges.length(); ++i)
+ ss << " [" << ranges.start(i, IGNORE_EXCEPTION) << "," << ranges.end(i, IGNORE_EXCEPTION) << ")";
+ ss << " }";
+
+ return ss.str();
+}
+
+#define ASSERT_RANGE(expected, range) EXPECT_EQ(expected, ToString(*range))
+
+TEST(TimeRanges, Empty)
+{
+ ASSERT_RANGE("{ }", TimeRanges::create().ptr());
+}
+
+TEST(TimeRanges, SingleRange)
+{
+ ASSERT_RANGE("{ [1,2) }", TimeRanges::create(1, 2).ptr());
+}
+
+TEST(TimeRanges, AddOrder)
+{
+ RefPtr<TimeRanges> rangeA = TimeRanges::create();
+ RefPtr<TimeRanges> rangeB = TimeRanges::create();
+
+ rangeA->add(0, 2);
+ rangeA->add(3, 4);
+ rangeA->add(5, 100);
+
+ std::string expected = "{ [0,2) [3,4) [5,100) }";
+ ASSERT_RANGE(expected, rangeA);
+
+ // Add the values in rangeA to rangeB in reverse order.
+ for (int i = rangeA->length() - 1; i >= 0; --i)
+ rangeB->add(rangeA->start(i, IGNORE_EXCEPTION), rangeA->end(i, IGNORE_EXCEPTION));
+
+ ASSERT_RANGE(expected, rangeB);
+}
+
+TEST(TimeRanges, OverlappingAdds)
+{
+ RefPtr<TimeRanges> ranges = TimeRanges::create();
+
+ ranges->add(0, 2);
+ ranges->add(10, 11);
+ ASSERT_RANGE("{ [0,2) [10,11) }", ranges);
+
+ ranges->add(0, 2);
+ ASSERT_RANGE("{ [0,2) [10,11) }", ranges);
+
+ ranges->add(2, 3);
+ ASSERT_RANGE("{ [0,3) [10,11) }", ranges);
+
+ ranges->add(2, 6);
+ ASSERT_RANGE("{ [0,6) [10,11) }", ranges);
+
+ ranges->add(9, 10);
+ ASSERT_RANGE("{ [0,6) [9,11) }", ranges);
+
+ ranges->add(8, 10);
+ ASSERT_RANGE("{ [0,6) [8,11) }", ranges);
+
+ ranges->add(-1, 7);
+ ASSERT_RANGE("{ [-1,7) [8,11) }", ranges);
+
+ ranges->add(6, 9);
+ ASSERT_RANGE("{ [-1,11) }", ranges);
+}
+
+TEST(TimeRanges, IntersectWith_Self)
+{
+ RefPtr<TimeRanges> ranges = TimeRanges::create(0, 2);
+
+ ASSERT_RANGE("{ [0,2) }", ranges);
+
+ ranges->intersectWith(*ranges.get());
+
+ ASSERT_RANGE("{ [0,2) }", ranges);
+}
+
+TEST(TimeRanges, IntersectWith_IdenticalRange)
+{
+ RefPtr<TimeRanges> rangesA = TimeRanges::create(0, 2);
+ RefPtr<TimeRanges> rangesB = rangesA->copy();
+
+ ASSERT_RANGE("{ [0,2) }", rangesA);
+ ASSERT_RANGE("{ [0,2) }", rangesB);
+
+ rangesA->intersectWith(*rangesB.get());
+
+ ASSERT_RANGE("{ [0,2) }", rangesA);
+ ASSERT_RANGE("{ [0,2) }", rangesB);
+}
+
+TEST(TimeRanges, IntersectWith_Empty)
+{
+ RefPtr<TimeRanges> rangesA = TimeRanges::create(0, 2);
+ RefPtr<TimeRanges> rangesB = TimeRanges::create();
+
+ ASSERT_RANGE("{ [0,2) }", rangesA);
+ ASSERT_RANGE("{ }", rangesB);
+
+ rangesA->intersectWith(*rangesB.get());
+
+ ASSERT_RANGE("{ }", rangesA);
+ ASSERT_RANGE("{ }", rangesB);
+}
+
+TEST(TimeRanges, IntersectWith_DisjointRanges1)
+{
+
+ RefPtr<TimeRanges> rangesA = TimeRanges::create();
+ RefPtr<TimeRanges> rangesB = TimeRanges::create();
+
+ rangesA->add(0, 1);
+ rangesA->add(4, 5);
+
+ rangesB->add(2, 3);
+ rangesB->add(6, 7);
+
+ ASSERT_RANGE("{ [0,1) [4,5) }", rangesA);
+ ASSERT_RANGE("{ [2,3) [6,7) }", rangesB);
+
+ rangesA->intersectWith(*rangesB.get());
+
+ ASSERT_RANGE("{ }", rangesA);
+ ASSERT_RANGE("{ [2,3) [6,7) }", rangesB);
+}
+
+TEST(TimeRanges, IntersectWith_DisjointRanges2)
+{
+ RefPtr<TimeRanges> rangesA = TimeRanges::create();
+ RefPtr<TimeRanges> rangesB = TimeRanges::create();
+
+ rangesA->add(0, 1);
+ rangesA->add(4, 5);
+
+ rangesB->add(1, 4);
+ rangesB->add(5, 7);
+
+ ASSERT_RANGE("{ [0,1) [4,5) }", rangesA);
+ ASSERT_RANGE("{ [1,4) [5,7) }", rangesB);
+
+ rangesA->intersectWith(*rangesB.get());
+
+ ASSERT_RANGE("{ }", rangesA);
+ ASSERT_RANGE("{ [1,4) [5,7) }", rangesB);
+}
+
+TEST(TimeRanges, IntersectWith_CompleteOverlap1)
+{
+ RefPtr<TimeRanges> rangesA = TimeRanges::create();
+ RefPtr<TimeRanges> rangesB = TimeRanges::create();
+
+ rangesA->add(1, 3);
+ rangesA->add(4, 5);
+ rangesA->add(6, 9);
+
+ rangesB->add(0, 10);
+
+ ASSERT_RANGE("{ [1,3) [4,5) [6,9) }", rangesA);
+ ASSERT_RANGE("{ [0,10) }", rangesB);
+
+ rangesA->intersectWith(*rangesB.get());
+
+ ASSERT_RANGE("{ [1,3) [4,5) [6,9) }", rangesA);
+ ASSERT_RANGE("{ [0,10) }", rangesB);
+}
+
+TEST(TimeRanges, IntersectWith_CompleteOverlap2)
+{
+ RefPtr<TimeRanges> rangesA = TimeRanges::create();
+ RefPtr<TimeRanges> rangesB = TimeRanges::create();
+
+ rangesA->add(1, 3);
+ rangesA->add(4, 5);
+ rangesA->add(6, 9);
+
+ rangesB->add(1, 9);
+
+ ASSERT_RANGE("{ [1,3) [4,5) [6,9) }", rangesA);
+ ASSERT_RANGE("{ [1,9) }", rangesB);
+
+ rangesA->intersectWith(*rangesB.get());
+
+ ASSERT_RANGE("{ [1,3) [4,5) [6,9) }", rangesA);
+ ASSERT_RANGE("{ [1,9) }", rangesB);
+}
+
+TEST(TimeRanges, IntersectWith_Gaps1)
+{
+ RefPtr<TimeRanges> rangesA = TimeRanges::create();
+ RefPtr<TimeRanges> rangesB = TimeRanges::create();
+
+ rangesA->add(0, 2);
+ rangesA->add(4, 6);
+
+ rangesB->add(1, 5);
+
+ ASSERT_RANGE("{ [0,2) [4,6) }", rangesA);
+ ASSERT_RANGE("{ [1,5) }", rangesB);
+
+ rangesA->intersectWith(*rangesB.get());
+
+ ASSERT_RANGE("{ [1,2) [4,5) }", rangesA);
+ ASSERT_RANGE("{ [1,5) }", rangesB);
+}
+
+TEST(TimeRanges, IntersectWith_Gaps2)
+{
+ RefPtr<TimeRanges> rangesA = TimeRanges::create();
+ RefPtr<TimeRanges> rangesB = TimeRanges::create();
+
+ rangesA->add(0, 2);
+ rangesA->add(4, 6);
+ rangesA->add(8, 10);
+
+ rangesB->add(1, 9);
+
+ ASSERT_RANGE("{ [0,2) [4,6) [8,10) }", rangesA);
+ ASSERT_RANGE("{ [1,9) }", rangesB);
+
+ rangesA->intersectWith(*rangesB.get());
+
+ ASSERT_RANGE("{ [1,2) [4,6) [8,9) }", rangesA);
+ ASSERT_RANGE("{ [1,9) }", rangesB);
+}
+
+TEST(TimeRanges, IntersectWith_Gaps3)
+{
+ RefPtr<TimeRanges> rangesA = TimeRanges::create();
+ RefPtr<TimeRanges> rangesB = TimeRanges::create();
+
+ rangesA->add(0, 2);
+ rangesA->add(4, 7);
+ rangesA->add(8, 10);
+
+ rangesB->add(1, 5);
+ rangesB->add(6, 9);
+
+ ASSERT_RANGE("{ [0,2) [4,7) [8,10) }", rangesA);
+ ASSERT_RANGE("{ [1,5) [6,9) }", rangesB);
+
+ rangesA->intersectWith(*rangesB.get());
+
+ ASSERT_RANGE("{ [1,2) [4,5) [6,7) [8,9) }", rangesA);
+ ASSERT_RANGE("{ [1,5) [6,9) }", rangesB);
+}
+
+}
+
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/URL.cpp b/Tools/TestWebKitAPI/Tests/WebCore/URL.cpp
new file mode 100644
index 000000000..4a7f53977
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/URL.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WTFStringUtilities.h"
+#include <WebCore/URL.h>
+#include <wtf/MainThread.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+class URLTest : public testing::Test {
+public:
+ virtual void SetUp()
+ {
+ WTF::initializeMainThread();
+ }
+};
+
+TEST_F(URLTest, URLConstructorDefault)
+{
+ URL kurl;
+
+ EXPECT_TRUE(kurl.isEmpty());
+ EXPECT_TRUE(kurl.isNull());
+ EXPECT_FALSE(kurl.isValid());
+}
+
+TEST_F(URLTest, URLConstructorConstChar)
+{
+ URL kurl(ParsedURLString, "http://username:password@www.example.com:8080/index.html?var=val#fragment");
+
+ EXPECT_FALSE(kurl.isEmpty());
+ EXPECT_FALSE(kurl.isNull());
+ EXPECT_TRUE(kurl.isValid());
+
+ EXPECT_EQ(String("http"), kurl.protocol());
+ EXPECT_EQ(String("www.example.com"), kurl.host());
+ EXPECT_TRUE(kurl.hasPort());
+ EXPECT_EQ(8080, kurl.port());
+ EXPECT_EQ(String("username"), kurl.user());
+ EXPECT_EQ(String("password"), kurl.pass());
+ EXPECT_EQ(String("/index.html"), kurl.path());
+ EXPECT_EQ(String("index.html"), kurl.lastPathComponent());
+ EXPECT_EQ(String("var=val"), kurl.query());
+ EXPECT_TRUE(kurl.hasFragmentIdentifier());
+ EXPECT_EQ(String("fragment"), kurl.fragmentIdentifier());
+}
+
+TEST_F(URLTest, URLDataURIStringSharing)
+{
+ URL baseURL(ParsedURLString, "http://www.webkit.org/");
+ String threeApples = "data:text/plain;charset=utf-8;base64,76O/76O/76O/";
+
+ URL url(baseURL, threeApples);
+ EXPECT_EQ(threeApples.impl(), url.string().impl());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/gtk/UserAgentQuirks.cpp b/Tools/TestWebKitAPI/Tests/WebCore/gtk/UserAgentQuirks.cpp
new file mode 100644
index 000000000..4033b37e2
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebCore/gtk/UserAgentQuirks.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <WebCore/URL.h>
+#include <WebCore/UserAgentGtk.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+TEST(WebCore, UserAgentQuirksTest)
+{
+ // A site with not quirks should return a null String.
+ String uaString = standardUserAgentForURL(URL(ParsedURLString, "http://www.webkit.org/"));
+ EXPECT_TRUE(uaString.isNull());
+
+ // www.yahoo.com requires MAC OS platform in the UA.
+ uaString = standardUserAgentForURL(URL(ParsedURLString, "http://www.yahoo.com/"));
+ EXPECT_TRUE(uaString.contains("Macintosh"));
+ EXPECT_TRUE(uaString.contains("Mac OS X"));
+ EXPECT_FALSE(uaString.contains("Linux"));
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/18-characters.html b/Tools/TestWebKitAPI/Tests/WebKit2/18-characters.html
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/18-characters.html
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/AboutBlankLoad.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/AboutBlankLoad.cpp
new file mode 100644
index 000000000..5503edd9e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/AboutBlankLoad.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+namespace TestWebKitAPI {
+
+static bool done;
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ WKRetainPtr<WKStringRef> mimeType = adoptWK(WKFrameCopyMIMEType(frame));
+ EXPECT_WK_STREQ("text/html", mimeType);
+ done = true;
+}
+
+TEST(WebKit2, AboutBlankLoad)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPageLoadURL(webView.page(), adoptWK(WKURLCreateWithUTF8CString("about:blank")).get());
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CanHandleRequest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/CanHandleRequest.cpp
new file mode 100644
index 000000000..a1d09f2cd
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CanHandleRequest.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKContextPrivate.h>
+
+namespace TestWebKitAPI {
+
+static bool didReceiveMessage;
+static bool canHandleRequest;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef body, const void*)
+{
+ didReceiveMessage = true;
+
+ EXPECT_WK_STREQ("DidCheckCanHandleRequest", messageName);
+ EXPECT_EQ(WKBooleanGetTypeID(), WKGetTypeID(body));
+
+ canHandleRequest = WKBooleanGetValue(static_cast<WKBooleanRef>(body));
+}
+
+static void setInjectedBundleClient(WKContextRef context)
+{
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+}
+
+TEST(WebKit2, CanHandleRequest)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("CanHandleRequestTest"));
+ setInjectedBundleClient(context.get());
+
+ WKContextRegisterURLSchemeAsEmptyDocument(context.get(), Util::toWK("emptyscheme").get());
+
+ PlatformWebView webView(context.get());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple", "html")).get());
+
+ WKContextPostMessageToInjectedBundle(context.get(), Util::toWK("CheckCanHandleRequest").get(), 0);
+ Util::run(&didReceiveMessage);
+ EXPECT_TRUE(canHandleRequest);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CanHandleRequest_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/CanHandleRequest_Bundle.cpp
new file mode 100644
index 000000000..590615e8c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CanHandleRequest_Bundle.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+
+namespace TestWebKitAPI {
+
+class CanHandleRequestTest : public InjectedBundleTest {
+public:
+ CanHandleRequestTest(const std::string& identifier);
+
+private:
+ virtual void didReceiveMessage(WKBundleRef, WKStringRef messageName, WKTypeRef messageBody);
+};
+
+static InjectedBundleTest::Register<CanHandleRequestTest> registrar("CanHandleRequestTest");
+
+CanHandleRequestTest::CanHandleRequestTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+{
+}
+
+static bool canHandleURL(const char* url)
+{
+ return WKBundlePageCanHandleRequest(adoptWK(WKURLRequestCreateWithWKURL(adoptWK(WKURLCreateWithUTF8CString(url)).get())).get());
+}
+
+static bool runTest()
+{
+ return canHandleURL("about:blank") && canHandleURL("emptyscheme://") && !canHandleURL("notascheme://");
+}
+
+void CanHandleRequestTest::didReceiveMessage(WKBundleRef bundle, WKStringRef messageName, WKTypeRef)
+{
+ if (!WKStringIsEqualToUTF8CString(messageName, "CheckCanHandleRequest"))
+ return;
+
+ WKBundlePostMessage(bundle, Util::toWK("DidCheckCanHandleRequest").get(), adoptWK(WKBooleanCreate(runTest())).get());
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CloseFromWithinCreatePage.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/CloseFromWithinCreatePage.cpp
new file mode 100644
index 000000000..b5e4a901b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CloseFromWithinCreatePage.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+namespace TestWebKitAPI {
+
+static bool testDone;
+static std::unique_ptr<PlatformWebView> openedWebView;
+
+static void runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, WKSecurityOriginRef, const void* clientInfo)
+{
+ // FIXME: Check that the alert text matches the storage.
+ testDone = true;
+}
+
+static WKPageRef createNewPage(WKPageRef page, WKURLRequestRef urlRequest, WKDictionaryRef features, WKEventModifiers modifiers, WKEventMouseButton mouseButton, const void *clientInfo)
+{
+ EXPECT_TRUE(openedWebView == nullptr);
+
+ openedWebView = std::make_unique<PlatformWebView>(page);
+
+ WKPageUIClientV5 uiClient;
+ memset(&uiClient, 0, sizeof(uiClient));
+
+ uiClient.base.version = 5;
+ uiClient.runJavaScriptAlert = runJavaScriptAlert;
+ WKPageSetPageUIClient(openedWebView->page(), &uiClient.base);
+
+ WKPageClose(page);
+
+ WKRetain(openedWebView->page());
+ return openedWebView->page();
+}
+
+TEST(WebKit2, CloseFromWithinCreatePage)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ PlatformWebView webView(context.get());
+
+ WKPageUIClientV5 uiClient;
+ memset(&uiClient, 0, sizeof(uiClient));
+
+ uiClient.base.version = 5;
+ uiClient.createNewPage = createNewPage;
+ uiClient.runJavaScriptAlert = runJavaScriptAlert;
+ WKPageSetPageUIClient(webView.page(), &uiClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("close-from-within-create-page", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&testDone);
+
+ openedWebView = nullptr;
+}
+
+}
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CloseThenTerminate.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/CloseThenTerminate.cpp
new file mode 100644
index 000000000..71239e4f0
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CloseThenTerminate.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+namespace TestWebKitAPI {
+
+static bool loaded;
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ loaded = true;
+}
+
+// Test for https://webkit.org/b/115607
+// This tests that we don't crash when calling WKPageClose and WKPageTerminate.
+
+TEST(WebKit2, CloseThenTerminate)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0 , sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&loaded);
+
+ WKPageClose(webView.page());
+ WKPageTerminate(webView.page());
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CookieManager.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/CookieManager.cpp
new file mode 100644
index 000000000..d231e0e75
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CookieManager.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKCookieManager.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool testDone;
+// Make sure that the policy on the machine running the test is not changed after running the test.
+static WKHTTPCookieAcceptPolicy userPolicy;
+static WKHTTPCookieAcceptPolicy testPolicy;
+static WKRetainPtr<WKContextRef> wkContext;
+
+static void didGetTestHTTPCookieAcceptPolicy(WKHTTPCookieAcceptPolicy policy, WKErrorRef, void* context)
+{
+ EXPECT_EQ(reinterpret_cast<void*>(0x1234578), context);
+ EXPECT_EQ(testPolicy, policy);
+
+ WKCookieManagerRef cookieManager = WKContextGetCookieManager(wkContext.get());
+ WKCookieManagerSetHTTPCookieAcceptPolicy(cookieManager, userPolicy);
+
+ testDone = true;
+}
+
+static void didGetUserHTTPCookieAcceptPolicy(WKHTTPCookieAcceptPolicy policy, WKErrorRef, void* context)
+{
+ EXPECT_EQ(reinterpret_cast<void*>(0x1234578), context);
+
+ userPolicy = policy;
+
+ // Make sure to choose a policy different from the policy the user currently has set.
+ testPolicy = (userPolicy + 1) % 3;
+ WKCookieManagerRef cookieManager = WKContextGetCookieManager(wkContext.get());
+ WKCookieManagerSetHTTPCookieAcceptPolicy(cookieManager, testPolicy);
+ WKCookieManagerGetHTTPCookieAcceptPolicy(cookieManager, reinterpret_cast<void*>(0x1234578), didGetTestHTTPCookieAcceptPolicy);
+}
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ WKCookieManagerRef cookieManager = WKContextGetCookieManager(wkContext.get());
+ WKCookieManagerGetHTTPCookieAcceptPolicy(cookieManager, reinterpret_cast<void*>(0x1234578), didGetUserHTTPCookieAcceptPolicy);
+}
+
+TEST(WebKit2, CookieManager)
+{
+ wkContext.adopt(WKContextCreate());
+ PlatformWebView webView(wkContext.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPageLoadURL(webView.page(), adoptWK(WKURLCreateWithUTF8CString("about:blank")).get());
+
+ Util::run(&testDone);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewIsActiveSetIsActive.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewIsActiveSetIsActive.cpp
new file mode 100644
index 000000000..ef9625b99
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewIsActiveSetIsActive.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PlatformUtilities.h"
+#include "Test.h"
+
+#include <WebKit/WKContext.h>
+#include <WebKit/WKPage.h>
+#include <WebKit/WKRetainPtr.h>
+#include <WebKit/WKView.h>
+
+namespace TestWebKitAPI {
+
+static bool didWebProcessCrash = false;
+static bool didWebProcessRelaunch = false;
+static bool didFinishLoad = false;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void webProcessCrashed(WKViewRef view, WKURLRef, const void*)
+{
+ // WebProcess crashed, so at this point the view should not be active.
+ ASSERT_FALSE(WKViewIsActive(view));
+ didWebProcessCrash = true;
+}
+
+static void webProcessRelaunched(WKViewRef view, const void*)
+{
+ // WebProcess just relaunched, so at this point the view should not be active.
+ ASSERT_FALSE(WKViewIsActive(view));
+
+ didWebProcessRelaunch = true;
+}
+
+TEST(WebKit2, WKViewIsActiveSetIsActive)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreate());
+ WKRetainPtr<WKViewRef> view = adoptWK(WKViewCreate(context.get(), 0));
+
+ WKViewInitialize(view.get());
+
+ // At this point we should have an active view.
+ ASSERT_TRUE(WKViewIsActive(view.get()));
+
+ // Now we are going to play with its active state a few times.
+ WKViewSetIsActive(view.get(), true);
+ ASSERT_TRUE(WKViewIsActive(view.get()));
+
+ WKViewSetIsActive(view.get(), false);
+ ASSERT_FALSE(WKViewIsActive(view.get()));
+
+ WKViewSetIsActive(view.get(), false);
+ ASSERT_FALSE(WKViewIsActive(view.get()));
+
+ WKViewSetIsActive(view.get(), true);
+ ASSERT_TRUE(WKViewIsActive(view.get()));
+}
+
+TEST(WebKit2, WKViewIsActive)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("WKViewIsActiveSetIsActiveTest"));
+ WKRetainPtr<WKViewRef> view = adoptWK(WKViewCreate(context.get(), 0));
+
+ WKViewClientV0 viewClient;
+ memset(&viewClient, 0, sizeof(WKViewClientV0));
+ viewClient.base.version = 0;
+ viewClient.webProcessCrashed = webProcessCrashed;
+ viewClient.webProcessDidRelaunch = webProcessRelaunched;
+ WKViewSetViewClient(view.get(), &viewClient.base);
+
+ WKViewInitialize(view.get());
+
+ // At this point we should have an active view.
+ ASSERT_TRUE(WKViewIsActive(view.get()));
+
+ WKPageLoaderClientV3 pageLoaderClient;
+ memset(&pageLoaderClient, 0, sizeof(WKPageLoaderClient));
+ pageLoaderClient.base.version = 3;
+ pageLoaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+ WKPageSetPageLoaderClient(WKViewGetPage(view.get()), &pageLoaderClient.base);
+
+ const WKSize size = WKSizeMake(100, 100);
+ WKViewSetSize(view.get(), size);
+
+ didFinishLoad = false;
+ didWebProcessCrash = false;
+ didWebProcessRelaunch = false;
+
+ WKRetainPtr<WKURLRef> simpleUrl = adoptWK(Util::createURLForResource("../WebKit/simple", "html"));
+ WKPageLoadURL(WKViewGetPage(view.get()), simpleUrl.get());
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ WKContextPostMessageToInjectedBundle(context.get(), Util::toWK("Crash").get(), 0);
+ Util::run(&didWebProcessCrash);
+ ASSERT_TRUE(didWebProcessCrash);
+
+ WKPageLoadURL(WKViewGetPage(view.get()), simpleUrl.get());
+ Util::run(&didFinishLoad);
+
+ ASSERT_TRUE(didWebProcessRelaunch);
+ ASSERT_TRUE(didFinishLoad);
+}
+
+} // TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewIsActiveSetIsActive_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewIsActiveSetIsActive_Bundle.cpp
new file mode 100644
index 000000000..a3cb1b6de
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewIsActiveSetIsActive_Bundle.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InjectedBundleTest.h"
+#include <WebKit/WKRetainPtr.h>
+
+#include <cstdlib>
+
+namespace TestWebKitAPI {
+
+class WKViewIsActiveSetIsActiveTest : public InjectedBundleTest {
+public:
+ WKViewIsActiveSetIsActiveTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void didReceiveMessage(WKBundleRef bundle, WKStringRef messageName, WKTypeRef messageBody)
+ {
+ if (!WKStringIsEqualToUTF8CString(messageName, "Crash"))
+ return;
+ abort();
+ }
+};
+
+static InjectedBundleTest::Register<WKViewIsActiveSetIsActiveTest> registrar("WKViewIsActiveSetIsActiveTest");
+
+} // namespace TestWebKitAPI
+
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewRestoreZoomAndScrollBackForward.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewRestoreZoomAndScrollBackForward.cpp
new file mode 100644
index 000000000..5c556ecee
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewRestoreZoomAndScrollBackForward.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2012-2013 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2011 Samsung Electronics. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "ewk_view_private.h"
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKContext.h>
+#include <WebKit/WKRetainPtr.h>
+#include "Test.h"
+
+namespace TestWebKitAPI {
+
+static bool finishedLoad = false;
+static bool scroll = false;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ finishedLoad = true;
+}
+
+static void didChangeContentsPosition(WKViewRef, WKPoint p, const void*)
+{
+ scroll = true;
+}
+
+TEST(WebKit2, WKViewRestoreZoomAndScrollBackForward)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+ WKRetainPtr<WKViewRef> view = EWKViewGetWKView(webView.platformView());
+
+ WKPageSetUseFixedLayout(webView.page(), true);
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKViewClientV0 viewClient;
+ memset(&viewClient, 0, sizeof(viewClient));
+ viewClient.base.version = 0;
+ viewClient.didChangeContentsPosition = didChangeContentsPosition;
+ WKViewSetViewClient(view.get(), &viewClient.base);
+
+ // Load first page.
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("CoordinatedGraphics/backforward1", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+ Util::run(&finishedLoad);
+
+ // Change scale and position on first page.
+ float firstPageScale = 2.0;
+ WKPoint firstPageScrollPosition = WKPointMake(10, 350); // Scroll position of first page.
+ WKViewSetContentPosition(view.get(), firstPageScrollPosition);
+ WKViewSetContentScaleFactor(view.get(), firstPageScale);
+ float currentPageScale = WKViewGetContentScaleFactor(view.get());
+ WKPoint currentPagePosition = WKViewGetContentPosition(view.get());
+ Util::run(&scroll);
+ EXPECT_EQ(firstPageScale, currentPageScale);
+ EXPECT_EQ(firstPageScrollPosition.x, currentPagePosition.x);
+ EXPECT_EQ(firstPageScrollPosition.y, currentPagePosition.y);
+
+ // Load second page.
+ finishedLoad = false;
+ url = adoptWK(Util::createURLForResource("CoordinatedGraphics/backforward2", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+ Util::run(&finishedLoad);
+
+ // Check if second page scale and position is correct.
+ currentPageScale = WKViewGetContentScaleFactor(view.get());
+ currentPagePosition = WKViewGetContentPosition(view.get());
+ EXPECT_EQ(1, currentPageScale);
+ EXPECT_EQ(0, currentPagePosition.x);
+ EXPECT_EQ(0, currentPagePosition.y);
+
+ // Go back first page.
+ scroll = false;
+ finishedLoad = false;
+ WKPageGoBack(webView.page());
+ Util::run(&finishedLoad);
+ Util::run(&scroll);
+
+ // Check if scroll position and scale of first page are restored correctly.
+ currentPageScale = WKViewGetContentScaleFactor(view.get());
+ currentPagePosition = WKViewGetContentPosition(view.get());
+ EXPECT_EQ(firstPageScale, currentPageScale);
+ EXPECT_EQ(firstPageScrollPosition.x, currentPagePosition.x);
+ EXPECT_EQ(firstPageScrollPosition.y, currentPagePosition.y);
+
+ // Go to second page again.
+ WKPageGoForward(webView.page());
+ scroll = false;
+ finishedLoad = false;
+ Util::run(&finishedLoad);
+
+ // Check if the scroll position and scale of second page are restored correctly.
+ currentPageScale = WKViewGetContentScaleFactor(view.get());
+ currentPagePosition = WKViewGetContentPosition(view.get());
+ EXPECT_EQ(1, currentPageScale);
+ EXPECT_EQ(0, currentPagePosition.x);
+ EXPECT_EQ(0, currentPagePosition.y);
+}
+
+} // TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewUserViewportToContents.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewUserViewportToContents.cpp
new file mode 100644
index 000000000..70899b8da
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/WKViewUserViewportToContents.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "WebKit/WKView.h"
+#include "WebKit/WKRetainPtr.h"
+
+namespace TestWebKitAPI {
+
+TEST(WebKit2, DISABLED_WKViewUserViewportToContents)
+{
+ // This test creates a WKView and uses the WKViewUserViewportToContents
+ // function to convert viewport coordinates to contents (page) coordinates.
+ // It scrolls and scales around the contents and check if the coordinates
+ // conversion math is right.
+
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ WKRetainPtr<WKViewRef> webView(AdoptWK, WKViewCreate(context.get(), 0));
+
+ WKViewInitialize(webView.get());
+ WKPageSetUseFixedLayout(WKViewGetPage(webView.get()), false);
+
+ WKPoint out;
+
+ // At scale 1.0 the viewport and contents coordinates should match.
+
+ WKViewSetContentScaleFactor(webView.get(), 1.0);
+ WKViewSetContentPosition(webView.get(), WKPointMake(0, 0));
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(0, 0));
+ EXPECT_EQ(out.x, 0);
+ EXPECT_EQ(out.y, 0);
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(10, 10));
+ EXPECT_EQ(out.x, 10);
+ EXPECT_EQ(out.y, 10);
+
+ WKViewSetContentPosition(webView.get(), WKPointMake(20, 20));
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(0, 0));
+ EXPECT_EQ(out.x, 20);
+ EXPECT_EQ(out.y, 20);
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(10, 10));
+ EXPECT_EQ(out.x, 30);
+ EXPECT_EQ(out.y, 30);
+
+ // At scale 2.0 the viewport distance values will be half
+ // the ones seem in the contents.
+
+ WKViewSetContentScaleFactor(webView.get(), 2.0);
+ WKViewSetContentPosition(webView.get(), WKPointMake(0, 0));
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(0, 0));
+ EXPECT_EQ(out.x, 0);
+ EXPECT_EQ(out.y, 0);
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(10, 10));
+ EXPECT_EQ(out.x, 5);
+ EXPECT_EQ(out.y, 5);
+
+ WKViewSetContentPosition(webView.get(), WKPointMake(20, 20));
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(0, 0));
+ EXPECT_EQ(out.x, 20);
+ EXPECT_EQ(out.y, 20);
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(10, 10));
+ EXPECT_EQ(out.x, 25);
+ EXPECT_EQ(out.y, 25);
+
+ // At scale 0.5 the viewport distance values will be twice
+ // the ones seem in the contents.
+
+ WKViewSetContentScaleFactor(webView.get(), 0.5);
+ WKViewSetContentPosition(webView.get(), WKPointMake(0, 0));
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(0, 0));
+ EXPECT_EQ(out.x, 0);
+ EXPECT_EQ(out.y, 0);
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(10, 10));
+ EXPECT_EQ(out.x, 20);
+ EXPECT_EQ(out.y, 20);
+
+ WKViewSetContentPosition(webView.get(), WKPointMake(20, 20));
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(0, 0));
+ EXPECT_EQ(out.x, 20);
+ EXPECT_EQ(out.y, 20);
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(10, 10));
+ EXPECT_EQ(out.x, 40);
+ EXPECT_EQ(out.y, 40);
+
+ // Let's add translation to the viewport.
+
+ const int delta = 10;
+ WKViewSetUserViewportTranslation(webView.get(), delta, delta);
+
+ WKViewSetContentPosition(webView.get(), WKPointMake(0, 0));
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(0, 0));
+ EXPECT_EQ(out.x, 0 - delta / 0.5);
+ EXPECT_EQ(out.y, 0 - delta / 0.5);
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(10, 10));
+ EXPECT_EQ(out.x, 20 - delta / 0.5);
+ EXPECT_EQ(out.y, 20 - delta / 0.5);
+
+ WKViewSetContentPosition(webView.get(), WKPointMake(20, 20));
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(0, 0));
+ EXPECT_EQ(out.x, 20 - delta / 0.5);
+ EXPECT_EQ(out.y, 20 - delta / 0.5);
+
+ out = WKViewUserViewportToContents(webView.get(), WKPointMake(10, 10));
+ EXPECT_EQ(out.x, 40 - delta / 0.5);
+ EXPECT_EQ(out.y, 40 - delta / 0.5);
+}
+
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/backforward1.html b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/backforward1.html
new file mode 100644
index 000000000..98c1c107c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/backforward1.html
@@ -0,0 +1,5 @@
+<html>
+<body style="width:100%; height:100%">
+ <div style="position: absolute; top: 0; left: 0; width: 900px; height: 1200px;"></div>
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/backforward2.html b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/backforward2.html
new file mode 100644
index 000000000..3806b9d32
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/CoordinatedGraphics/backforward2.html
@@ -0,0 +1 @@
+<body style="width:100%; height:100%"/>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic.cpp
new file mode 100644
index 000000000..c336652ac
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+#include <wtf/Vector.h>
+
+namespace TestWebKitAPI {
+
+static bool finished;
+
+static const char* expectedMessages[] = {
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"Subframe finished loading",
+"Extension states:\nFirst page, main frame, standard world - Connected\nFirst page, main frame, non-standard world - Connected\nFirst page, subframe, standard world - Connected\nFirst page, subframe, non-standard world - Connected\nSecond page, main frame, standard world - Uncreated\nSecond page, main frame, non-standard world - Uncreated",
+"Main frame finished loading",
+"Extension states:\nFirst page, main frame, standard world - Connected\nFirst page, main frame, non-standard world - Connected\nFirst page, subframe, standard world - Connected\nFirst page, subframe, non-standard world - Connected\nSecond page, main frame, standard world - Uncreated\nSecond page, main frame, non-standard world - Uncreated",
+"WillDisconnectDOMWindowExtensionFromGlobalObject called",
+"WillDisconnectDOMWindowExtensionFromGlobalObject called",
+"WillDisconnectDOMWindowExtensionFromGlobalObject called",
+"WillDisconnectDOMWindowExtensionFromGlobalObject called",
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"Main frame finished loading",
+"Extension states:\nFirst page, main frame, standard world - Disconnected\nFirst page, main frame, non-standard world - Disconnected\nFirst page, subframe, standard world - Disconnected\nFirst page, subframe, non-standard world - Disconnected\nSecond page, main frame, standard world - Connected\nSecond page, main frame, non-standard world - Connected",
+"WillDisconnectDOMWindowExtensionFromGlobalObject called",
+"WillDisconnectDOMWindowExtensionFromGlobalObject called",
+"DidReconnectDOMWindowExtensionToGlobalObject called",
+"DidReconnectDOMWindowExtensionToGlobalObject called",
+"DidReconnectDOMWindowExtensionToGlobalObject called",
+"DidReconnectDOMWindowExtensionToGlobalObject called",
+"Main frame finished loading",
+"Extension states:\nFirst page, main frame, standard world - Connected\nFirst page, main frame, non-standard world - Connected\nFirst page, subframe, standard world - Connected\nFirst page, subframe, non-standard world - Connected\nSecond page, main frame, standard world - Disconnected\nSecond page, main frame, non-standard world - Disconnected",
+"Extension states:\nFirst page, main frame, standard world - Removed\nFirst page, main frame, non-standard world - Removed\nFirst page, subframe, standard world - Removed\nFirst page, subframe, non-standard world - Removed\nSecond page, main frame, standard world - Removed\nSecond page, main frame, non-standard world - Removed",
+"TestComplete"
+};
+
+static Vector<WKRetainPtr<WKStringRef> > messages;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef messageBody, const void*)
+{
+ ASSERT_NOT_NULL(messageBody);
+ EXPECT_EQ(WKStringGetTypeID(), WKGetTypeID(messageBody));
+
+ WKStringRef bodyString = (WKStringRef)messageBody;
+ messages.append(bodyString);
+
+ if (WKStringIsEqualToUTF8CString(messageName, "DidFinishLoadForMainFrame") || WKStringIsEqualToUTF8CString(messageName, "TestComplete"))
+ finished = true;
+}
+
+TEST(WebKit2, DISABLED_DOMWindowExtensionBasic)
+{
+ WKRetainPtr<WKPageGroupRef> pageGroup(AdoptWK, WKPageGroupCreateWithIdentifier(WKStringCreateWithUTF8CString("DOMWindowExtensionBasicPageGroup")));
+
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("DOMWindowExtensionBasic", pageGroup.get()));
+
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context.get(), &injectedBundleClient.base);
+
+ // The default cache model has a capacity of 0, so it is necessary to switch to a cache
+ // model that actually allows for a page cache.
+ WKContextSetCacheModel(context.get(), kWKCacheModelDocumentBrowser);
+
+ PlatformWebView webView(context.get(), pageGroup.get());
+
+ // Make sure the extensions for each frame are installed in each world.
+ WKRetainPtr<WKURLRef> url1(AdoptWK, Util::createURLForResource("simple-iframe", "html"));
+ WKPageLoadURL(webView.page(), url1.get());
+
+ Util::run(&finished);
+ finished = false;
+
+ // Make sure those first 4 extensions are disconnected, and 2 new ones are installed.
+ WKRetainPtr<WKURLRef> url2(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url2.get());
+
+ Util::run(&finished);
+ finished = false;
+
+ // Make sure those two are disconnected, and the first four are reconnected.
+ WKPageGoBack(webView.page());
+
+ Util::run(&finished);
+ finished = false;
+
+ // Make sure the 2 disconnected extensions in the page cache and the 4 active extensions are all removed.
+ WKPageClose(webView.page());
+
+ Util::run(&finished);
+
+ const size_t expectedSize = sizeof(expectedMessages) / sizeof(const char*);
+ EXPECT_EQ(expectedSize, messages.size());
+
+ if (messages.size() != expectedSize)
+ return;
+
+ for (size_t i = 0; i < messages.size(); ++i)
+ EXPECT_WK_STREQ(expectedMessages[i], messages[i].get());
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic_Bundle.cpp
new file mode 100644
index 000000000..9bdc683ba
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionBasic_Bundle.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include <WebKit/WKBundleDOMWindowExtension.h>
+#include <WebKit/WKBundleFrame.h>
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundlePageGroup.h>
+#include <WebKit/WKBundlePrivate.h>
+#include <WebKit/WKBundleScriptWorld.h>
+#include <WebKit/WKRetainPtr.h>
+#include <wtf/HashMap.h>
+#include <assert.h>
+
+namespace TestWebKitAPI {
+
+static void didFinishLoadForFrameCallback(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void* clientInfo);
+static void globalObjectIsAvailableForFrameCallback(WKBundlePageRef, WKBundleFrameRef, WKBundleScriptWorldRef, const void* clientInfo);
+static void willDisconnectDOMWindowExtensionFromGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo);
+static void didReconnectDOMWindowExtensionToGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo);
+static void willDestroyGlobalObjectForDOMWindowExtensionCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo);
+
+
+enum ExtensionState {
+ Uncreated = 0, Connected, Disconnected, Destroyed, Removed
+};
+
+const char* stateNames[5] = {
+ "Uncreated",
+ "Connected",
+ "Disconnected",
+ "Destroyed",
+ "Removed"
+};
+
+typedef struct {
+ const char* name;
+ ExtensionState state;
+} ExtensionRecord;
+
+class DOMWindowExtensionBasic : public InjectedBundleTest {
+public:
+ DOMWindowExtensionBasic(const std::string& identifier);
+
+ virtual void initialize(WKBundleRef, WKTypeRef userData);
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef);
+ virtual void willDestroyPage(WKBundleRef, WKBundlePageRef);
+
+ void globalObjectIsAvailableForFrame(WKBundleFrameRef, WKBundleScriptWorldRef);
+ void willDisconnectDOMWindowExtensionFromGlobalObject(WKBundleDOMWindowExtensionRef);
+ void didReconnectDOMWindowExtensionToGlobalObject(WKBundleDOMWindowExtensionRef);
+ void willDestroyGlobalObjectForDOMWindowExtension(WKBundleDOMWindowExtensionRef);
+
+ void frameLoadFinished(WKBundleFrameRef);
+
+private:
+ void updateExtensionStateRecord(WKBundleDOMWindowExtensionRef, ExtensionState);
+ void sendExtensionStateMessage();
+ void sendBundleMessage(const char*);
+
+ WKBundleRef m_bundle;
+ ExtensionRecord m_extensionRecords[6];
+ HashMap<WKBundleDOMWindowExtensionRef, int> m_extensionToRecordMap;
+ bool m_finishedOneMainFrameLoad;
+};
+
+static InjectedBundleTest::Register<DOMWindowExtensionBasic> registrar("DOMWindowExtensionBasic");
+
+DOMWindowExtensionBasic::DOMWindowExtensionBasic(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ , m_finishedOneMainFrameLoad(false)
+{
+ m_extensionRecords[0].name = "First page, main frame, standard world";
+ m_extensionRecords[1].name = "First page, main frame, non-standard world";
+ m_extensionRecords[2].name = "First page, subframe, standard world";
+ m_extensionRecords[3].name = "First page, subframe, non-standard world";
+ m_extensionRecords[4].name = "Second page, main frame, standard world";
+ m_extensionRecords[5].name = "Second page, main frame, non-standard world";
+
+ for (size_t i = 0; i < 6; ++i)
+ m_extensionRecords[i].state = Uncreated;
+}
+
+void DOMWindowExtensionBasic::frameLoadFinished(WKBundleFrameRef frame)
+{
+ bool mainFrame = !WKBundleFrameGetParentFrame(frame);
+ if (mainFrame)
+ m_finishedOneMainFrameLoad = true;
+
+ char body[16384];
+ sprintf(body, "%s finished loading", mainFrame ? "Main frame" : "Subframe");
+
+ // Only consider load finished for the main frame
+ const char* name = mainFrame ? "DidFinishLoadForMainFrame" : "DidFinishLoadForFrame";
+
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString(name));
+ WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithUTF8CString(body));
+ WKBundlePostMessage(m_bundle, messageName.get(), messageBody.get());
+
+ sendExtensionStateMessage();
+}
+
+void DOMWindowExtensionBasic::sendExtensionStateMessage()
+{
+ char body[16384];
+ sprintf(body, "Extension states:\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s",
+ m_extensionRecords[0].name, stateNames[m_extensionRecords[0].state],
+ m_extensionRecords[1].name, stateNames[m_extensionRecords[1].state],
+ m_extensionRecords[2].name, stateNames[m_extensionRecords[2].state],
+ m_extensionRecords[3].name, stateNames[m_extensionRecords[3].state],
+ m_extensionRecords[4].name, stateNames[m_extensionRecords[4].state],
+ m_extensionRecords[5].name, stateNames[m_extensionRecords[5].state]);
+
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("ExtensionStates"));
+ WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithUTF8CString(body));
+ WKBundlePostMessage(m_bundle, messageName.get(), messageBody.get());
+}
+
+void DOMWindowExtensionBasic::initialize(WKBundleRef bundle, WKTypeRef userData)
+{
+ assert(WKGetTypeID(userData) == WKBundlePageGroupGetTypeID());
+ WKBundlePageGroupRef pageGroup = static_cast<WKBundlePageGroupRef>(userData);
+
+ WKRetainPtr<WKStringRef> source(AdoptWK, WKStringCreateWithUTF8CString("alert('Unimportant alert');"));
+ WKBundleAddUserScript(bundle, pageGroup, WKBundleScriptWorldCreateWorld(), source.get(), 0, 0, 0, kWKInjectAtDocumentStart, kWKInjectInAllFrames);
+}
+
+void DOMWindowExtensionBasic::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+{
+ m_bundle = bundle;
+
+ WKBundlePageLoaderClientV1 pageLoaderClient;
+ memset(&pageLoaderClient, 0, sizeof(pageLoaderClient));
+
+ pageLoaderClient.base.version = 1;
+ pageLoaderClient.base.clientInfo = this;
+ pageLoaderClient.didFinishLoadForFrame = didFinishLoadForFrameCallback;
+ pageLoaderClient.globalObjectIsAvailableForFrame = globalObjectIsAvailableForFrameCallback;
+ pageLoaderClient.willDisconnectDOMWindowExtensionFromGlobalObject = willDisconnectDOMWindowExtensionFromGlobalObjectCallback;
+ pageLoaderClient.didReconnectDOMWindowExtensionToGlobalObject = didReconnectDOMWindowExtensionToGlobalObjectCallback;
+ pageLoaderClient.willDestroyGlobalObjectForDOMWindowExtension = willDestroyGlobalObjectForDOMWindowExtensionCallback;
+
+ WKBundlePageSetPageLoaderClient(page, &pageLoaderClient.base);
+}
+
+void DOMWindowExtensionBasic::willDestroyPage(WKBundleRef, WKBundlePageRef)
+{
+ HashMap<WKBundleDOMWindowExtensionRef, int>::iterator it = m_extensionToRecordMap.begin();
+ HashMap<WKBundleDOMWindowExtensionRef, int>::iterator end = m_extensionToRecordMap.end();
+ for (; it != end; ++it) {
+ updateExtensionStateRecord(it->key, Removed);
+ WKRelease(it->key);
+ }
+
+ m_extensionToRecordMap.clear();
+
+ sendExtensionStateMessage();
+ sendBundleMessage("TestComplete");
+}
+
+void DOMWindowExtensionBasic::updateExtensionStateRecord(WKBundleDOMWindowExtensionRef extension, ExtensionState state)
+{
+ int index = m_extensionToRecordMap.get(extension);
+ m_extensionRecords[index].state = state;
+}
+
+void DOMWindowExtensionBasic::sendBundleMessage(const char* message)
+{
+ WKRetainPtr<WKStringRef> wkMessage = adoptWK(WKStringCreateWithUTF8CString(message));
+ WKBundlePostMessage(m_bundle, wkMessage.get(), wkMessage.get());
+}
+
+void DOMWindowExtensionBasic::globalObjectIsAvailableForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
+{
+ WKBundleDOMWindowExtensionRef extension = WKBundleDOMWindowExtensionCreate(frame, world);
+
+ int index;
+ bool standard;
+ standard = world == WKBundleScriptWorldNormalWorld();
+
+ if (WKBundleFrameGetParentFrame(frame))
+ index = standard ? 2 : 3;
+ else
+ index = m_finishedOneMainFrameLoad ? (standard ? 4 : 5) : (standard ? 0 : 1);
+
+ m_extensionToRecordMap.set(extension, index);
+
+ updateExtensionStateRecord(extension, Connected);
+ sendBundleMessage("GlobalObjectIsAvailableForFrame called");
+}
+
+void DOMWindowExtensionBasic::willDisconnectDOMWindowExtensionFromGlobalObject(WKBundleDOMWindowExtensionRef extension)
+{
+ updateExtensionStateRecord(extension, Disconnected);
+ sendBundleMessage("WillDisconnectDOMWindowExtensionFromGlobalObject called");
+}
+
+void DOMWindowExtensionBasic::didReconnectDOMWindowExtensionToGlobalObject(WKBundleDOMWindowExtensionRef extension)
+{
+ updateExtensionStateRecord(extension, Connected);
+ sendBundleMessage("DidReconnectDOMWindowExtensionToGlobalObject called");
+}
+
+void DOMWindowExtensionBasic::willDestroyGlobalObjectForDOMWindowExtension(WKBundleDOMWindowExtensionRef)
+{
+ // All of the items are candidates for the page cache and should not be evicted from the page
+ // cache before the test completes.
+ ASSERT_NOT_REACHED();
+}
+
+static void didFinishLoadForFrameCallback(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
+{
+ ((DOMWindowExtensionBasic*)clientInfo)->frameLoadFinished(frame);
+}
+
+static void globalObjectIsAvailableForFrameCallback(WKBundlePageRef, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void* clientInfo)
+{
+ ((DOMWindowExtensionBasic*)clientInfo)->globalObjectIsAvailableForFrame(frame, world);
+}
+
+static void willDisconnectDOMWindowExtensionFromGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension, const void* clientInfo)
+{
+ ((DOMWindowExtensionBasic*)clientInfo)->willDisconnectDOMWindowExtensionFromGlobalObject(extension);
+}
+
+static void didReconnectDOMWindowExtensionToGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension, const void* clientInfo)
+{
+ ((DOMWindowExtensionBasic*)clientInfo)->didReconnectDOMWindowExtensionToGlobalObject(extension);
+}
+
+static void willDestroyGlobalObjectForDOMWindowExtensionCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension , const void* clientInfo)
+{
+ ((DOMWindowExtensionBasic*)clientInfo)->willDestroyGlobalObjectForDOMWindowExtension(extension);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionNoCache.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionNoCache.cpp
new file mode 100644
index 000000000..56a069fb9
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionNoCache.cpp
@@ -0,0 +1,139 @@
+/*
+* Copyright (C) 2012 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+* THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <wtf/Vector.h>
+
+namespace TestWebKitAPI {
+
+static bool finished;
+
+static const char* expectedMessages[] = {
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"Subframe finished loading",
+"Extension states:\nFirst page, main frame, standard world - Connected\nFirst page, main frame, non-standard world - Connected\nFirst page, subframe, standard world - Connected\nFirst page, subframe, non-standard world - Connected\nSecond page, main frame, standard world - Uncreated\nSecond page, main frame, non-standard world - Uncreated\nFirst page, main frame, standard world - Uncreated\nFirst page, main frame, non-standard world - Uncreated\nFirst page, subframe, standard world - Uncreated\nFirst page, subframe, non-standard world - Uncreated",
+"Main frame finished loading",
+"Extension states:\nFirst page, main frame, standard world - Connected\nFirst page, main frame, non-standard world - Connected\nFirst page, subframe, standard world - Connected\nFirst page, subframe, non-standard world - Connected\nSecond page, main frame, standard world - Uncreated\nSecond page, main frame, non-standard world - Uncreated\nFirst page, main frame, standard world - Uncreated\nFirst page, main frame, non-standard world - Uncreated\nFirst page, subframe, standard world - Uncreated\nFirst page, subframe, non-standard world - Uncreated",
+"WillDestroyDOMWindowExtensionToGlobalObject called",
+"WillDestroyDOMWindowExtensionToGlobalObject called",
+"WillDestroyDOMWindowExtensionToGlobalObject called",
+"WillDestroyDOMWindowExtensionToGlobalObject called",
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"Main frame finished loading",
+"Extension states:\nFirst page, main frame, standard world - Destroyed\nFirst page, main frame, non-standard world - Destroyed\nFirst page, subframe, standard world - Destroyed\nFirst page, subframe, non-standard world - Destroyed\nSecond page, main frame, standard world - Connected\nSecond page, main frame, non-standard world - Connected\nFirst page, main frame, standard world - Uncreated\nFirst page, main frame, non-standard world - Uncreated\nFirst page, subframe, standard world - Uncreated\nFirst page, subframe, non-standard world - Uncreated",
+"WillDestroyDOMWindowExtensionToGlobalObject called",
+"WillDestroyDOMWindowExtensionToGlobalObject called",
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"GlobalObjectIsAvailableForFrame called",
+"Subframe finished loading",
+"Extension states:\nFirst page, main frame, standard world - Destroyed\nFirst page, main frame, non-standard world - Destroyed\nFirst page, subframe, standard world - Destroyed\nFirst page, subframe, non-standard world - Destroyed\nSecond page, main frame, standard world - Destroyed\nSecond page, main frame, non-standard world - Destroyed\nFirst page, main frame, standard world - Connected\nFirst page, main frame, non-standard world - Connected\nFirst page, subframe, standard world - Connected\nFirst page, subframe, non-standard world - Connected",
+"Main frame finished loading",
+"Extension states:\nFirst page, main frame, standard world - Destroyed\nFirst page, main frame, non-standard world - Destroyed\nFirst page, subframe, standard world - Destroyed\nFirst page, subframe, non-standard world - Destroyed\nSecond page, main frame, standard world - Destroyed\nSecond page, main frame, non-standard world - Destroyed\nFirst page, main frame, standard world - Connected\nFirst page, main frame, non-standard world - Connected\nFirst page, subframe, standard world - Connected\nFirst page, subframe, non-standard world - Connected",
+"Extension states:\nFirst page, main frame, standard world - Destroyed\nFirst page, main frame, non-standard world - Destroyed\nFirst page, subframe, standard world - Destroyed\nFirst page, subframe, non-standard world - Destroyed\nSecond page, main frame, standard world - Destroyed\nSecond page, main frame, non-standard world - Destroyed\nFirst page, main frame, standard world - Removed\nFirst page, main frame, non-standard world - Removed\nFirst page, subframe, standard world - Removed\nFirst page, subframe, non-standard world - Removed",
+"TestComplete"
+};
+
+static Vector<WKRetainPtr<WKStringRef> > messages;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef messageBody, const void*)
+{
+ ASSERT_NOT_NULL(messageBody);
+ EXPECT_EQ(WKStringGetTypeID(), WKGetTypeID(messageBody));
+
+ WKStringRef bodyString = (WKStringRef)messageBody;
+ messages.append(bodyString);
+
+ if (WKStringIsEqualToUTF8CString(messageName, "DidFinishLoadForMainFrame") || WKStringIsEqualToUTF8CString(messageName, "TestComplete"))
+ finished = true;
+}
+
+TEST(WebKit2, DISABLED_DOMWindowExtensionNoCache)
+{
+ WKRetainPtr<WKPageGroupRef> pageGroup(AdoptWK, WKPageGroupCreateWithIdentifier(WKStringCreateWithUTF8CString("DOMWindowExtensionNoCachePageGroup")));
+
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("DOMWindowExtensionNoCache", pageGroup.get()));
+
+ WKContextInjectedBundleClientV1 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 1;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context.get(), &injectedBundleClient.base);
+
+ // Disable the page cache.
+ WKContextSetCacheModel(context.get(), kWKCacheModelDocumentViewer);
+
+ PlatformWebView webView(context.get(), pageGroup.get());
+
+ // Make sure the extensions for each frame are installed in each world.
+ WKRetainPtr<WKURLRef> url1(AdoptWK, Util::createURLForResource("simple-iframe", "html"));
+ WKPageLoadURL(webView.page(), url1.get());
+
+ Util::run(&finished);
+ finished = false;
+
+ // Make sure those first 4 extensions are destroyed, and 2 new ones are installed.
+ WKRetainPtr<WKURLRef> url2(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url2.get());
+
+ Util::run(&finished);
+ finished = false;
+
+ // Make sure those 2 are destroyed, and the first 4 are recreated.
+ WKPageGoBack(webView.page());
+
+ Util::run(&finished);
+ finished = false;
+
+ WKPageClose(webView.page());
+
+ Util::run(&finished);
+
+ const size_t expectedSize = sizeof(expectedMessages) / sizeof(const char*);
+ EXPECT_EQ(expectedSize, messages.size());
+
+ if (messages.size() != expectedSize)
+ return;
+
+ for (size_t i = 0; i < messages.size(); ++i)
+ EXPECT_WK_STREQ(expectedMessages[i], messages[i].get());
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionNoCache_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionNoCache_Bundle.cpp
new file mode 100644
index 000000000..e48dadd1e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/DOMWindowExtensionNoCache_Bundle.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include <WebKit/WKBundleDOMWindowExtension.h>
+#include <WebKit/WKBundleFrame.h>
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundlePageGroup.h>
+#include <WebKit/WKBundlePrivate.h>
+#include <WebKit/WKBundleScriptWorld.h>
+#include <WebKit/WKRetainPtr.h>
+#include <wtf/HashMap.h>
+#include <assert.h>
+
+namespace TestWebKitAPI {
+
+static void didFinishLoadForFrameCallback(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void* clientInfo);
+static void globalObjectIsAvailableForFrameCallback(WKBundlePageRef, WKBundleFrameRef, WKBundleScriptWorldRef, const void* clientInfo);
+static void willDisconnectDOMWindowExtensionFromGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo);
+static void didReconnectDOMWindowExtensionToGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo);
+static void willDestroyGlobalObjectForDOMWindowExtensionCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo);
+
+enum ExtensionState {
+ Uncreated = 0, Connected, Disconnected, Destroyed, Removed
+};
+
+const char* states[5] = {
+ "Uncreated",
+ "Connected",
+ "Disconnected",
+ "Destroyed",
+ "Removed"
+};
+
+typedef struct {
+ const char* name;
+ ExtensionState state;
+} ExtensionRecord;
+
+class DOMWindowExtensionNoCache : public InjectedBundleTest {
+public:
+ DOMWindowExtensionNoCache(const std::string& identifier);
+
+ virtual void initialize(WKBundleRef, WKTypeRef userData);
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef);
+ virtual void willDestroyPage(WKBundleRef, WKBundlePageRef);
+
+ void globalObjectIsAvailableForFrame(WKBundleFrameRef, WKBundleScriptWorldRef);
+ void willDisconnectDOMWindowExtensionFromGlobalObject(WKBundleDOMWindowExtensionRef);
+ void didReconnectDOMWindowExtensionToGlobalObject(WKBundleDOMWindowExtensionRef);
+ void willDestroyGlobalObjectForDOMWindowExtension(WKBundleDOMWindowExtensionRef);
+
+ void frameLoadFinished(WKBundleFrameRef);
+
+private:
+ void updateExtensionStateRecord(WKBundleDOMWindowExtensionRef, ExtensionState);
+ void sendExtensionStateMessage();
+ void sendBundleMessage(const char*);
+
+ WKBundleRef m_bundle;
+ ExtensionRecord m_extensionRecords[10];
+ HashMap<WKBundleDOMWindowExtensionRef, int> m_extensionToRecordMap;
+ int m_numberMainFrameLoads;
+};
+
+static InjectedBundleTest::Register<DOMWindowExtensionNoCache> registrar("DOMWindowExtensionNoCache");
+
+DOMWindowExtensionNoCache::DOMWindowExtensionNoCache(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ , m_numberMainFrameLoads(0)
+{
+ m_extensionRecords[0].name = "First page, main frame, standard world";
+ m_extensionRecords[1].name = "First page, main frame, non-standard world";
+ m_extensionRecords[2].name = "First page, subframe, standard world";
+ m_extensionRecords[3].name = "First page, subframe, non-standard world";
+ m_extensionRecords[4].name = "Second page, main frame, standard world";
+ m_extensionRecords[5].name = "Second page, main frame, non-standard world";
+ m_extensionRecords[6].name = "First page, main frame, standard world";
+ m_extensionRecords[7].name = "First page, main frame, non-standard world";
+ m_extensionRecords[8].name = "First page, subframe, standard world";
+ m_extensionRecords[9].name = "First page, subframe, non-standard world";
+
+ for (size_t i = 0; i < 10; ++i)
+ m_extensionRecords[i].state = Uncreated;
+}
+
+void DOMWindowExtensionNoCache::frameLoadFinished(WKBundleFrameRef frame)
+{
+ bool mainFrame = !WKBundleFrameGetParentFrame(frame);
+ if (mainFrame)
+ m_numberMainFrameLoads++;
+
+ char body[16384];
+ sprintf(body, "%s finished loading", mainFrame ? "Main frame" : "Subframe");
+
+ // Only consider load finished for the main frame
+ const char* name = mainFrame ? "DidFinishLoadForMainFrame" : "DidFinishLoadForFrame";
+
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString(name));
+ WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithUTF8CString(body));
+ WKBundlePostMessage(m_bundle, messageName.get(), messageBody.get());
+
+ sendExtensionStateMessage();
+}
+
+void DOMWindowExtensionNoCache::sendExtensionStateMessage()
+{
+ char body[16384];
+ sprintf(body, "Extension states:\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s",
+ m_extensionRecords[0].name, states[m_extensionRecords[0].state],
+ m_extensionRecords[1].name, states[m_extensionRecords[1].state],
+ m_extensionRecords[2].name, states[m_extensionRecords[2].state],
+ m_extensionRecords[3].name, states[m_extensionRecords[3].state],
+ m_extensionRecords[4].name, states[m_extensionRecords[4].state],
+ m_extensionRecords[5].name, states[m_extensionRecords[5].state],
+ m_extensionRecords[6].name, states[m_extensionRecords[6].state],
+ m_extensionRecords[7].name, states[m_extensionRecords[7].state],
+ m_extensionRecords[8].name, states[m_extensionRecords[8].state],
+ m_extensionRecords[9].name, states[m_extensionRecords[9].state]);
+
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("ExtensionStates"));
+ WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithUTF8CString(body));
+ WKBundlePostMessage(m_bundle, messageName.get(), messageBody.get());
+}
+
+void DOMWindowExtensionNoCache::initialize(WKBundleRef bundle, WKTypeRef userData)
+{
+ assert(WKGetTypeID(userData) == WKBundlePageGroupGetTypeID());
+ WKBundlePageGroupRef pageGroup = static_cast<WKBundlePageGroupRef>(userData);
+
+ WKRetainPtr<WKStringRef> source(AdoptWK, WKStringCreateWithUTF8CString("alert('Unimportant alert');"));
+ WKBundleAddUserScript(bundle, pageGroup, WKBundleScriptWorldCreateWorld(), source.get(), 0, 0, 0, kWKInjectAtDocumentStart, kWKInjectInAllFrames);
+}
+
+void DOMWindowExtensionNoCache::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+{
+ m_bundle = bundle;
+
+ WKBundlePageLoaderClientV7 pageLoaderClient;
+ memset(&pageLoaderClient, 0, sizeof(pageLoaderClient));
+
+ pageLoaderClient.base.version = 7;
+ pageLoaderClient.base.clientInfo = this;
+ pageLoaderClient.didFinishLoadForFrame = didFinishLoadForFrameCallback;
+ pageLoaderClient.globalObjectIsAvailableForFrame = globalObjectIsAvailableForFrameCallback;
+ pageLoaderClient.willDisconnectDOMWindowExtensionFromGlobalObject = willDisconnectDOMWindowExtensionFromGlobalObjectCallback;
+ pageLoaderClient.didReconnectDOMWindowExtensionToGlobalObject = didReconnectDOMWindowExtensionToGlobalObjectCallback;
+ pageLoaderClient.willDestroyGlobalObjectForDOMWindowExtension = willDestroyGlobalObjectForDOMWindowExtensionCallback;
+
+ WKBundlePageSetPageLoaderClient(page, &pageLoaderClient.base);
+}
+
+void DOMWindowExtensionNoCache::willDestroyPage(WKBundleRef, WKBundlePageRef)
+{
+ HashMap<WKBundleDOMWindowExtensionRef, int>::iterator it = m_extensionToRecordMap.begin();
+ HashMap<WKBundleDOMWindowExtensionRef, int>::iterator end = m_extensionToRecordMap.end();
+ for (; it != end; ++it) {
+ updateExtensionStateRecord(it->key, Removed);
+ WKRelease(it->key);
+ }
+
+ m_extensionToRecordMap.clear();
+
+ sendExtensionStateMessage();
+ sendBundleMessage("TestComplete");
+}
+
+void DOMWindowExtensionNoCache::updateExtensionStateRecord(WKBundleDOMWindowExtensionRef extension, ExtensionState state)
+{
+ int index = m_extensionToRecordMap.get(extension);
+ m_extensionRecords[index].state = state;
+}
+
+void DOMWindowExtensionNoCache::sendBundleMessage(const char* message)
+{
+ WKRetainPtr<WKStringRef> wkMessage = adoptWK(WKStringCreateWithUTF8CString(message));
+ WKBundlePostMessage(m_bundle, wkMessage.get(), wkMessage.get());
+}
+
+void DOMWindowExtensionNoCache::globalObjectIsAvailableForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
+{
+ WKBundleDOMWindowExtensionRef extension = WKBundleDOMWindowExtensionCreate(frame, world);
+
+ int index = 0;
+ bool standard;
+ standard = world == WKBundleScriptWorldNormalWorld();
+
+ bool mainFrame = !WKBundleFrameGetParentFrame(frame);
+ switch (m_numberMainFrameLoads) {
+ case 0:
+ index = mainFrame ? (standard ? 0 : 1) : (standard ? 2 : 3);
+ break;
+ case 1:
+ index = standard ? 4 : 5;
+ break;
+ case 2:
+ index = mainFrame ? (standard ? 6 : 7) : (standard ? 8 : 9);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ m_extensionToRecordMap.set(extension, index);
+
+ updateExtensionStateRecord(extension, Connected);
+ sendBundleMessage("GlobalObjectIsAvailableForFrame called");
+}
+
+void DOMWindowExtensionNoCache::willDisconnectDOMWindowExtensionFromGlobalObject(WKBundleDOMWindowExtensionRef extension)
+{
+ // No items should be going into a 0-capacity page cache.
+ ASSERT_NOT_REACHED();
+}
+
+void DOMWindowExtensionNoCache::didReconnectDOMWindowExtensionToGlobalObject(WKBundleDOMWindowExtensionRef)
+{
+ // No items should be coming out of a 0-capacity page cache.
+ ASSERT_NOT_REACHED();
+}
+
+void DOMWindowExtensionNoCache::willDestroyGlobalObjectForDOMWindowExtension(WKBundleDOMWindowExtensionRef extension)
+{
+ sendBundleMessage("WillDestroyDOMWindowExtensionToGlobalObject called");
+ updateExtensionStateRecord(extension, Destroyed);
+ m_extensionToRecordMap.remove(extension);
+ WKRelease(extension);
+}
+
+static void didFinishLoadForFrameCallback(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
+{
+ ((DOMWindowExtensionNoCache*)clientInfo)->frameLoadFinished(frame);
+}
+
+static void globalObjectIsAvailableForFrameCallback(WKBundlePageRef, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void* clientInfo)
+{
+ ((DOMWindowExtensionNoCache*)clientInfo)->globalObjectIsAvailableForFrame(frame, world);
+}
+
+static void willDisconnectDOMWindowExtensionFromGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension, const void* clientInfo)
+{
+ ((DOMWindowExtensionNoCache*)clientInfo)->willDisconnectDOMWindowExtensionFromGlobalObject(extension);
+}
+
+static void didReconnectDOMWindowExtensionToGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension, const void* clientInfo)
+{
+ ((DOMWindowExtensionNoCache*)clientInfo)->didReconnectDOMWindowExtensionToGlobalObject(extension);
+}
+
+static void willDestroyGlobalObjectForDOMWindowExtensionCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension , const void* clientInfo)
+{
+ ((DOMWindowExtensionNoCache*)clientInfo)->willDestroyGlobalObjectForDOMWindowExtension(extension);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/DidAssociateFormControls.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/DidAssociateFormControls.cpp
new file mode 100644
index 000000000..a930f5346
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/DidAssociateFormControls.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+
+namespace TestWebKitAPI {
+
+static bool didReceiveAllMessages = false;
+static bool receivedMessageForAddingForm = false;
+static const uint64_t expectedNumberOfElements = 1;
+
+static void nullJavaScriptCallback(WKSerializedScriptValueRef, WKErrorRef, void*)
+{
+}
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef messageBody, const void*)
+{
+ EXPECT_WK_STREQ("DidReceiveDidAssociateFormControls", messageName);
+ ASSERT_NOT_NULL(messageBody);
+ EXPECT_EQ(WKDictionaryGetTypeID(), WKGetTypeID(messageBody));
+
+ WKDictionaryRef dictionary = static_cast<WKDictionaryRef>(messageBody);
+ uint64_t numberOfElements = WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(dictionary, Util::toWK("NumberOfControls").get())));
+
+ EXPECT_EQ(expectedNumberOfElements, numberOfElements);
+
+ if (!receivedMessageForAddingForm) {
+ receivedMessageForAddingForm = true;
+
+ WKPageRef page = static_cast<WKPageRef>(WKDictionaryGetItemForKey(dictionary, Util::toWK("Page").get()));
+ WKPageRunJavaScriptInMainFrame(page, Util::toWK("addPasswordFieldToForm()").get(), 0, nullJavaScriptCallback);
+
+ return;
+ }
+
+ didReceiveAllMessages = true;
+}
+
+static void setInjectedBundleClient(WKContextRef context)
+{
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+}
+
+TEST(WebKit2, DidAssociateFormControls)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("DidAssociateFormControlsTest"));
+ setInjectedBundleClient(context.get());
+
+ PlatformWebView webView(context.get());
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("associate-form-controls", "html")).get());
+ Util::run(&didReceiveAllMessages);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/DidAssociateFormControls_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/DidAssociateFormControls_Bundle.cpp
new file mode 100644
index 000000000..5dcd91da8
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/DidAssociateFormControls_Bundle.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundle.h>
+#include <WebKit/WKBundlePage.h>
+
+namespace TestWebKitAPI {
+
+class DidAssociateFormControlsTest : public InjectedBundleTest {
+public:
+ DidAssociateFormControlsTest(const std::string& identifier);
+
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef) override;
+};
+
+static InjectedBundleTest::Register<DidAssociateFormControlsTest> registrar("DidAssociateFormControlsTest");
+
+static bool shouldNotifyOnFormChanges(WKBundlePageRef, const void*)
+{
+ return true;
+}
+
+static void didAssociateFormControls(WKBundlePageRef page, WKArrayRef elementHandles, const void*)
+{
+ WKRetainPtr<WKMutableDictionaryRef> messageBody = adoptWK(WKMutableDictionaryCreate());
+
+ WKDictionarySetItem(messageBody.get(), Util::toWK("Page").get(), page);
+ WKRetainPtr<WKUInt64Ref> numberOfElements = adoptWK(WKUInt64Create(WKArrayGetSize(elementHandles)));
+ WKDictionarySetItem(messageBody.get(), Util::toWK("NumberOfControls").get(), numberOfElements.get());
+
+ WKBundlePostMessage(InjectedBundleController::singleton().bundle(), Util::toWK("DidReceiveDidAssociateFormControls").get(), messageBody.get());
+}
+
+DidAssociateFormControlsTest::DidAssociateFormControlsTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+{
+}
+
+void DidAssociateFormControlsTest::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+{
+ WKBundlePageFormClientV2 formClient;
+ memset(&formClient, 0, sizeof(formClient));
+
+ formClient.base.version = 2;
+ formClient.base.clientInfo = this;
+ formClient.shouldNotifyOnFormChanges = shouldNotifyOnFormChanges;
+ formClient.didAssociateFormControls = didAssociateFormControls;
+
+ WKBundlePageSetFormClient(page, &formClient.base);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/DidNotHandleKeyDown.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/DidNotHandleKeyDown.cpp
new file mode 100644
index 000000000..b5f4fb863
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/DidNotHandleKeyDown.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "JavaScriptTest.h"
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool didFinishTest;
+static bool didNotHandleKeyDownEvent;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+}
+
+static void didNotHandleKeyEventCallback(WKPageRef, WKNativeEventPtr event, const void*)
+{
+ if (Util::isKeyDown(event))
+ didNotHandleKeyDownEvent = true;
+ didFinishTest = true;
+}
+
+TEST(WebKit2, DidNotHandleKeyDown)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextWithInjectedBundle());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPageUIClientV0 uiClient;
+ memset(&uiClient, 0, sizeof(uiClient));
+
+ uiClient.base.version = 0;
+ uiClient.didNotHandleKeyEvent = didNotHandleKeyEventCallback;
+
+ WKPageSetPageUIClient(webView.page(), &uiClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ webView.simulateSpacebarKeyPress();
+
+ Util::run(&didFinishTest);
+ EXPECT_TRUE(didNotHandleKeyDownEvent);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/DocumentStartUserScriptAlertCrash.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/DocumentStartUserScriptAlertCrash.cpp
new file mode 100644
index 000000000..21bca74dd
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/DocumentStartUserScriptAlertCrash.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool done;
+
+static void runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, const void* clientInfo)
+{
+ ASSERT_NOT_NULL(frame);
+
+ EXPECT_EQ(page, WKFrameGetPage(frame));
+ EXPECT_WK_STREQ("an alert", alertText);
+
+ done = true;
+}
+
+TEST(WebKit2, DocumentStartUserScriptAlertCrashTest)
+{
+ WKRetainPtr<WKPageGroupRef> pageGroup(AdoptWK, WKPageGroupCreateWithIdentifier(WKStringCreateWithUTF8CString("DocumentStartUserScriptAlertCrashTestPageGroup")));
+
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("DocumentStartUserScriptAlertCrashTest", pageGroup.get()));
+ PlatformWebView webView(context.get(), pageGroup.get());
+
+ WKPageUIClientV0 uiClient;
+ memset(&uiClient, 0, sizeof(uiClient));
+
+ uiClient.base.version = 0;
+ uiClient.runJavaScriptAlert = runJavaScriptAlert;
+
+ WKPageSetPageUIClient(webView.page(), &uiClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/DocumentStartUserScriptAlertCrash_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/DocumentStartUserScriptAlertCrash_Bundle.cpp
new file mode 100644
index 000000000..18b9f1b48
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/DocumentStartUserScriptAlertCrash_Bundle.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include <WebKit/WKBundlePageGroup.h>
+#include <WebKit/WKBundlePrivate.h>
+#include <WebKit/WKBundleScriptWorld.h>
+#include <WebKit/WKRetainPtr.h>
+#include <assert.h>
+
+namespace TestWebKitAPI {
+
+class DocumentStartUserScriptAlertCrashTest : public InjectedBundleTest {
+public:
+ DocumentStartUserScriptAlertCrashTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void initialize(WKBundleRef bundle, WKTypeRef userData)
+ {
+ assert(WKGetTypeID(userData) == WKBundlePageGroupGetTypeID());
+ WKBundlePageGroupRef pageGroup = static_cast<WKBundlePageGroupRef>(userData);
+
+ WKRetainPtr<WKStringRef> source(AdoptWK, WKStringCreateWithUTF8CString("alert('an alert');"));
+ WKBundleAddUserScript(bundle, pageGroup, WKBundleScriptWorldNormalWorld(), source.get(), 0, 0, 0, kWKInjectAtDocumentStart, kWKInjectInAllFrames);
+ }
+};
+
+static InjectedBundleTest::Register<DocumentStartUserScriptAlertCrashTest> registrar("DocumentStartUserScriptAlertCrashTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/DownloadDecideDestinationCrash.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/DownloadDecideDestinationCrash.cpp
new file mode 100644
index 000000000..eac36d87a
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/DownloadDecideDestinationCrash.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKDownload.h>
+
+namespace TestWebKitAPI {
+
+static bool didDecideDestination;
+
+static void decidePolicyForNavigationAction(WKPageRef, WKFrameRef, WKFrameNavigationType, WKEventModifiers, WKEventMouseButton, WKFrameRef, WKURLRequestRef, WKFramePolicyListenerRef listener, WKTypeRef, const void*)
+{
+ WKFramePolicyListenerDownload(listener);
+}
+
+static WKStringRef decideDestinationWithSuggestedFilename(WKContextRef, WKDownloadRef download, WKStringRef, bool*, const void*)
+{
+ didDecideDestination = true;
+ WKDownloadCancel(download);
+ return Util::toWK("/tmp/WebKitAPITest/DownloadDecideDestinationCrash").leakRef();
+}
+
+static void setContextDownloadClient(WKContextRef context)
+{
+ WKContextDownloadClientV0 client;
+ memset(&client, 0, sizeof(client));
+
+ client.base.version = 0;
+ client.decideDestinationWithSuggestedFilename = decideDestinationWithSuggestedFilename;
+
+ WKContextSetDownloadClient(context, &client.base);
+}
+
+static void setPagePolicyClient(WKPageRef page)
+{
+ WKPagePolicyClientV1 policyClient;
+ memset(&policyClient, 0, sizeof(policyClient));
+
+ policyClient.base.version = 1;
+ policyClient.decidePolicyForNavigationAction = decidePolicyForNavigationAction;
+
+ WKPageSetPagePolicyClient(page, &policyClient.base);
+}
+
+TEST(WebKit2, DownloadDecideDestinationCrash)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreate());
+ setContextDownloadClient(context.get());
+
+ PlatformWebView webView(context.get());
+ setPagePolicyClient(webView.page());
+
+ // The length of this filename was specially chosen to trigger the crash conditions in
+ // <http://webkit.org/b/61142>. Specifically, it causes ArgumentDecoder::m_bufferPos and m_bufferEnd
+ // to be equal after the DecideDestinationWithSuggestedFilename message has been handled.
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("18-characters", "html")).get());
+
+ Util::run(&didDecideDestination);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/EphemeralSessionPushStateNoHistoryCallback.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/EphemeralSessionPushStateNoHistoryCallback.cpp
new file mode 100644
index 000000000..07a81f646
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/EphemeralSessionPushStateNoHistoryCallback.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool testDone;
+
+static void didNavigateWithNavigationData(WKContextRef context, WKPageRef page, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
+{
+ // This should never be called when navigating in Private Browsing.
+ FAIL();
+}
+
+static void didSameDocumentNavigationForFrame(WKPageRef page, WKFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef userData, const void *clientInfo)
+{
+ testDone = true;
+}
+
+TEST(WebKit2, EphemeralSessionPushStateNoHistoryCallback)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ WKContextHistoryClientV0 historyClient;
+ memset(&historyClient, 0, sizeof(historyClient));
+
+ historyClient.base.version = 0;
+ historyClient.didNavigateWithNavigationData = didNavigateWithNavigationData;
+
+ WKContextSetHistoryClient(context.get(), &historyClient.base);
+
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 pageLoaderClient;
+ memset(&pageLoaderClient, 0, sizeof(pageLoaderClient));
+
+ pageLoaderClient.base.version = 0;
+ pageLoaderClient.didSameDocumentNavigationForFrame = didSameDocumentNavigationForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &pageLoaderClient.base);
+
+ WKSessionRef session = WKSessionCreate(true);
+ WKPageSetSession(webView.page(), session);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("push-state", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&testDone);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/EvaluateJavaScript.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/EvaluateJavaScript.cpp
new file mode 100644
index 000000000..a6b5a4dd5
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/EvaluateJavaScript.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+#include <WebKit/WKSerializedScriptValue.h>
+
+namespace TestWebKitAPI {
+
+static bool testDone;
+
+static void didRunJavaScript(WKSerializedScriptValueRef resultSerializedScriptValue, WKErrorRef error, void* context)
+{
+ EXPECT_EQ(reinterpret_cast<void*>(0x1234578), context);
+ EXPECT_NULL(resultSerializedScriptValue);
+
+ // FIXME: We should also check the error, but right now it's always null.
+ // Assert that it's null so we can revisit when this changes.
+ EXPECT_NULL(error);
+
+ testDone = true;
+}
+
+TEST(WebKit2, EvaluateJavaScriptThatThrowsAnException)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKRetainPtr<WKStringRef> javaScriptString(AdoptWK, WKStringCreateWithUTF8CString("throw 'Hello'"));
+ WKPageRunJavaScriptInMainFrame(webView.page(), javaScriptString.get(), reinterpret_cast<void*>(0x1234578), didRunJavaScript);
+
+ Util::run(&testDone);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/FailedLoad.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/FailedLoad.cpp
new file mode 100644
index 000000000..c12b5366b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/FailedLoad.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+// FIXME: This should also test the that the load state after didFailLoadWithErrorForFrame is kWKFrameLoadStateFinished
+
+static bool testDone;
+
+static void didFailProvisionalLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
+{
+ EXPECT_EQ(static_cast<uint32_t>(kWKFrameLoadStateFinished), WKFrameGetFrameLoadState(frame));
+
+ WKURLRef url = WKFrameCopyProvisionalURL(frame);
+ EXPECT_NULL(url);
+
+ testDone = true;
+}
+
+TEST(WebKit2, FailedLoad)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFailProvisionalLoadWithErrorForFrame = didFailProvisionalLoadWithErrorForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::URLForNonExistentResource());
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&testDone);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/Find.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/Find.cpp
new file mode 100644
index 000000000..2e2189906
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/Find.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool didFinishLoad = false;
+static bool didCallCountStringMatches = false;
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ didFinishLoad = true;
+}
+
+static void didCountStringMatches(WKPageRef page, WKStringRef string, unsigned numMatches, const void* clientInfo)
+{
+ EXPECT_WK_STREQ("Hello", string);
+ EXPECT_EQ(3u, numMatches);
+
+ didCallCountStringMatches = true;
+}
+
+TEST(WebKit2, Find)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPageFindClientV0 findClient;
+ memset(&findClient, 0, sizeof(findClient));
+
+ findClient.base.version = 0;
+ findClient.didCountStringMatches = didCountStringMatches;
+
+ WKPageSetPageFindClient(webView.page(), &findClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("find", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&didFinishLoad);
+
+ WKRetainPtr<WKStringRef> findString(AdoptWK, WKStringCreateWithUTF8CString("Hello"));
+ WKPageCountStringMatches(webView.page(), findString.get(), true, 100);
+
+ Util::run(&didCallCountStringMatches);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ForceRepaint.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ForceRepaint.cpp
new file mode 100644
index 000000000..6cd63fab4
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ForceRepaint.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool test1Done;
+static bool test2Done;
+
+void didForceRepaint(WKErrorRef error, void*)
+{
+ EXPECT_NULL(error);
+ test2Done = true;
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ test1Done = true;
+ WKPageForceRepaint(page, 0, didForceRepaint);
+}
+
+TEST(WebKit2, ForceRepaint)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple-accelerated-compositing", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&test1Done);
+ Util::run(&test2Done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/FrameMIMETypeHTML.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/FrameMIMETypeHTML.cpp
new file mode 100644
index 000000000..2c9c11332
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/FrameMIMETypeHTML.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool testDone;
+
+static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ WKRetainPtr<WKStringRef> wkMIME = adoptWK(WKFrameCopyMIMEType(frame));
+ EXPECT_TRUE(WKStringIsEmpty(wkMIME.get()));
+}
+
+static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ WKRetainPtr<WKStringRef> wkMIME = adoptWK(WKFrameCopyMIMEType(frame));
+ EXPECT_WK_STREQ("text/html", wkMIME);
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ WKRetainPtr<WKStringRef> wkMIME = adoptWK(WKFrameCopyMIMEType(frame));
+ EXPECT_WK_STREQ("text/html", wkMIME);
+
+ testDone = true;
+}
+
+TEST(WebKit2, FrameMIMETypeHTML)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
+ loaderClient.didCommitLoadForFrame = didCommitLoadForFrame;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&testDone);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/FrameMIMETypePNG.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/FrameMIMETypePNG.cpp
new file mode 100644
index 000000000..eb3bf38a3
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/FrameMIMETypePNG.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool testDone;
+
+static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ WKRetainPtr<WKStringRef> wkMIME = adoptWK(WKFrameCopyMIMEType(frame));
+ EXPECT_TRUE(WKStringIsEmpty(wkMIME.get()));
+}
+
+static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ WKRetainPtr<WKStringRef> wkMIME = adoptWK(WKFrameCopyMIMEType(frame));
+ EXPECT_WK_STREQ("image/png", wkMIME);
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ WKRetainPtr<WKStringRef> wkMIME = adoptWK(WKFrameCopyMIMEType(frame));
+ EXPECT_WK_STREQ("image/png", wkMIME);
+
+ testDone = true;
+}
+
+TEST(WebKit2, FrameMIMETypePNG)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
+ loaderClient.didCommitLoadForFrame = didCommitLoadForFrame;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("icon", "png"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&testDone);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/Geolocation.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/Geolocation.cpp
new file mode 100644
index 000000000..b6d872f4c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/Geolocation.cpp
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+#include <string.h>
+#include <vector>
+
+using namespace std;
+
+namespace TestWebKitAPI {
+
+enum class GeolocationEvent {
+ StartUpdating,
+ StopUpdating,
+ EnableHighAccuracy,
+ DisableHighAccuracy
+};
+
+ostream& operator<<(ostream& outputStream, const GeolocationEvent& geolocationEvent)
+{
+ switch (geolocationEvent) {
+ case GeolocationEvent::StartUpdating:
+ outputStream << "GeolocationEvent::StartUpdating";
+ break;
+ case GeolocationEvent::StopUpdating:
+ outputStream << "GeolocationEvent::StopUpdating";
+ break;
+ case GeolocationEvent::EnableHighAccuracy:
+ outputStream << "GeolocationEvent::EnableHighAccuracy";
+ break;
+ case GeolocationEvent::DisableHighAccuracy:
+ outputStream << "GeolocationEvent::DisableHighAccuracy";
+ break;
+ }
+ return outputStream;
+}
+
+struct GeolocationStateTracker {
+ vector<GeolocationEvent> events;
+
+ virtual ~GeolocationStateTracker() { }
+ virtual void eventsChanged() { }
+
+ static void startUpdatingCallback(WKGeolocationManagerRef manager, const void* clientInfo)
+ {
+ GeolocationStateTracker* stateTracker = static_cast<GeolocationStateTracker*>(const_cast<void*>(clientInfo));
+ stateTracker->events.push_back(GeolocationEvent::StartUpdating);
+ stateTracker->eventsChanged();
+
+ WKRetainPtr<WKGeolocationPositionRef> position = adoptWK(WKGeolocationPositionCreate(0, 50.644358, 3.345453, 2.53));
+ WKGeolocationManagerProviderDidChangePosition(manager, position.get());
+ }
+
+ static void stopUpdatingCallback(WKGeolocationManagerRef, const void* clientInfo)
+ {
+ GeolocationStateTracker* stateTracker = static_cast<GeolocationStateTracker*>(const_cast<void*>(clientInfo));
+ stateTracker->events.push_back(GeolocationEvent::StopUpdating);
+ stateTracker->eventsChanged();
+ }
+
+ static void setEnableHighAccuracyCallback(WKGeolocationManagerRef, bool enable, const void* clientInfo)
+ {
+ GeolocationStateTracker* stateTracker = static_cast<GeolocationStateTracker*>(const_cast<void*>(clientInfo));
+ if (enable)
+ stateTracker->events.push_back(GeolocationEvent::EnableHighAccuracy);
+ else
+ stateTracker->events.push_back(GeolocationEvent::DisableHighAccuracy);
+ stateTracker->eventsChanged();
+ }
+};
+
+void decidePolicyForGeolocationPermissionRequestCallBack(WKPageRef page, WKFrameRef frame, WKSecurityOriginRef origin, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
+{
+ WKGeolocationPermissionRequestAllow(permissionRequest);
+}
+
+void setupGeolocationProvider(WKContextRef context, void *clientInfo)
+{
+ WKGeolocationProviderV1 providerCallback;
+ memset(&providerCallback, 0, sizeof(WKGeolocationProviderV1));
+
+ providerCallback.base.version = 1;
+ providerCallback.base.clientInfo = clientInfo;
+ providerCallback.startUpdating = GeolocationStateTracker::startUpdatingCallback;
+ providerCallback.stopUpdating = GeolocationStateTracker::stopUpdatingCallback;
+ providerCallback.setEnableHighAccuracy = GeolocationStateTracker::setEnableHighAccuracyCallback;
+
+ WKGeolocationManagerSetProvider(WKContextGetGeolocationManager(context), &providerCallback.base);
+}
+
+void setupView(PlatformWebView& webView)
+{
+ WKPageUIClientV2 uiClient;
+ memset(&uiClient, 0, sizeof(uiClient));
+
+ uiClient.base.version = 2;
+ uiClient.decidePolicyForGeolocationPermissionRequest = decidePolicyForGeolocationPermissionRequestCallBack;
+
+ WKPageSetPageUIClient(webView.page(), &uiClient.base);
+}
+
+// GeolocationBasic.
+struct GeolocationBasicStateTracker : GeolocationStateTracker {
+ bool finished;
+
+ GeolocationBasicStateTracker() : finished(false) { }
+ virtual void eventsChanged()
+ {
+ switch (events.size()) {
+ case 1:
+ EXPECT_EQ(GeolocationEvent::DisableHighAccuracy, events[0]);
+ break;
+ case 2:
+ EXPECT_EQ(GeolocationEvent::StartUpdating, events[1]);
+ break;
+ case 3:
+ EXPECT_EQ(GeolocationEvent::StopUpdating, events[2]);
+ finished = true;
+ break;
+ default:
+ EXPECT_TRUE(false);
+ finished = true;
+ }
+ }
+};
+
+TEST(WebKit2, GeolocationBasic)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ GeolocationBasicStateTracker stateTracker;
+ setupGeolocationProvider(context.get(), &stateTracker);
+
+ PlatformWebView webView(context.get());
+ setupView(webView);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("geolocationGetCurrentPosition", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&stateTracker.finished);
+}
+
+// Geolocation requested with High Accuracy.
+struct GeolocationBasicWithHighAccuracyStateTracker : GeolocationStateTracker {
+ bool finished;
+
+ GeolocationBasicWithHighAccuracyStateTracker() : finished(false) { }
+ virtual void eventsChanged()
+ {
+ switch (events.size()) {
+ case 1:
+ EXPECT_EQ(GeolocationEvent::EnableHighAccuracy, events[0]);
+ break;
+ case 2:
+ EXPECT_EQ(GeolocationEvent::StartUpdating, events[1]);
+ break;
+ case 3:
+ EXPECT_EQ(GeolocationEvent::StopUpdating, events[2]);
+ finished = true;
+ break;
+ default:
+ EXPECT_TRUE(false);
+ finished = true;
+ }
+ }
+};
+
+TEST(WebKit2, GeolocationBasicWithHighAccuracy)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ GeolocationBasicWithHighAccuracyStateTracker stateTracker;
+ setupGeolocationProvider(context.get(), &stateTracker);
+
+ PlatformWebView webView(context.get());
+ setupView(webView);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("geolocationGetCurrentPositionWithHighAccuracy", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&stateTracker.finished);
+}
+
+// Geolocation start without High Accuracy, then requires High Accuracy.
+struct GeolocationTransitionToHighAccuracyStateTracker : GeolocationStateTracker {
+ bool finishedFirstStep;
+ bool finished;
+
+ GeolocationTransitionToHighAccuracyStateTracker()
+ : finishedFirstStep(false)
+ , finished(false)
+ {
+ }
+
+ virtual void eventsChanged()
+ {
+ switch (events.size()) {
+ case 1:
+ EXPECT_EQ(GeolocationEvent::DisableHighAccuracy, events[0]);
+ break;
+ case 2:
+ EXPECT_EQ(GeolocationEvent::StartUpdating, events[1]);
+ finishedFirstStep = true;
+ break;
+ case 3:
+ EXPECT_EQ(GeolocationEvent::EnableHighAccuracy, events[2]);
+ finished = true;
+ break;
+ default:
+ EXPECT_TRUE(false);
+ finishedFirstStep = true;
+ finished = true;
+ }
+ }
+};
+
+TEST(WebKit2, GeolocationTransitionToHighAccuracy)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ GeolocationTransitionToHighAccuracyStateTracker stateTracker;
+ setupGeolocationProvider(context.get(), &stateTracker);
+
+ PlatformWebView lowAccuracyWebView(context.get());
+ setupView(lowAccuracyWebView);
+ WKRetainPtr<WKURLRef> lowAccuracyURL(AdoptWK, Util::createURLForResource("geolocationWatchPosition", "html"));
+ WKPageLoadURL(lowAccuracyWebView.page(), lowAccuracyURL.get());
+ Util::run(&stateTracker.finishedFirstStep);
+
+ PlatformWebView highAccuracyWebView(context.get());
+ setupView(highAccuracyWebView);
+ WKRetainPtr<WKURLRef> highAccuracyURL(AdoptWK, Util::createURLForResource("geolocationWatchPositionWithHighAccuracy", "html"));
+ WKPageLoadURL(highAccuracyWebView.page(), highAccuracyURL.get());
+ Util::run(&stateTracker.finished);
+}
+
+// Geolocation start with High Accuracy, then should fall back to low accuracy.
+struct GeolocationTransitionToLowAccuracyStateTracker : GeolocationStateTracker {
+ bool finishedFirstStep;
+ bool finished;
+
+ GeolocationTransitionToLowAccuracyStateTracker()
+ : finishedFirstStep(false)
+ , finished(false)
+ {
+ }
+
+ virtual void eventsChanged()
+ {
+ switch (events.size()) {
+ case 1:
+ EXPECT_EQ(GeolocationEvent::EnableHighAccuracy, events[0]);
+ break;
+ case 2:
+ EXPECT_EQ(GeolocationEvent::StartUpdating, events[1]);
+ finishedFirstStep = true;
+ break;
+ case 3:
+ EXPECT_EQ(GeolocationEvent::DisableHighAccuracy, events[2]);
+ finished = true;
+ break;
+ default:
+ EXPECT_TRUE(false);
+ finishedFirstStep = true;
+ finished = true;
+ }
+ }
+};
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ *static_cast<bool*>(const_cast<void*>(clientInfo)) = true;
+}
+
+TEST(WebKit2, GeolocationTransitionToLowAccuracy)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ GeolocationTransitionToLowAccuracyStateTracker stateTracker;
+ setupGeolocationProvider(context.get(), &stateTracker);
+
+ PlatformWebView highAccuracyWebView(context.get());
+ setupView(highAccuracyWebView);
+ WKRetainPtr<WKURLRef> highAccuracyURL(AdoptWK, Util::createURLForResource("geolocationWatchPositionWithHighAccuracy", "html"));
+ WKPageLoadURL(highAccuracyWebView.page(), highAccuracyURL.get());
+ Util::run(&stateTracker.finishedFirstStep);
+
+ PlatformWebView lowAccuracyWebView(context.get());
+ setupView(lowAccuracyWebView);
+
+ bool finishedSecondStep = false;
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.base.clientInfo = &finishedSecondStep;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(lowAccuracyWebView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> lowAccuracyURL(AdoptWK, Util::createURLForResource("geolocationWatchPosition", "html"));
+ WKPageLoadURL(lowAccuracyWebView.page(), lowAccuracyURL.get());
+ Util::run(&finishedSecondStep);
+
+ WKRetainPtr<WKURLRef> resetUrl = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ WKPageLoadURL(highAccuracyWebView.page(), resetUrl.get());
+ Util::run(&stateTracker.finished);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/GetInjectedBundleInitializationUserDataCallback.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/GetInjectedBundleInitializationUserDataCallback.cpp
new file mode 100644
index 000000000..e3280a3b7
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/GetInjectedBundleInitializationUserDataCallback.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool done;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef, WKTypeRef messageBody, const void* clientInfo)
+{
+ EXPECT_WK_STREQ("Extra initialization data", static_cast<WKStringRef>(messageBody));
+ done = true;
+}
+
+static WKTypeRef getInjectedBundleInitializationUserData(WKContextRef context, const void* clientInfo)
+{
+ return Util::createInitializationDictionaryForInjectedBundleTest("GetInjectedBundleInitializationUserDataCallbackTest", Util::toWK("Extra initialization data").get());
+}
+
+TEST(WebKit2, GetInjectedBundleInitializationUserDataCallback)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextWithInjectedBundle());
+
+ WKContextInjectedBundleClientV1 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 1;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+ injectedBundleClient.getInjectedBundleInitializationUserData = getInjectedBundleInitializationUserData;
+
+ WKContextSetInjectedBundleClient(context.get(), &injectedBundleClient.base);
+
+ PlatformWebView webView(context.get());
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/GetInjectedBundleInitializationUserDataCallback_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/GetInjectedBundleInitializationUserDataCallback_Bundle.cpp
new file mode 100644
index 000000000..bf8da42e3
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/GetInjectedBundleInitializationUserDataCallback_Bundle.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include "PlatformUtilities.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+class GetInjectedBundleInitializationUserDataCallbackTest : public InjectedBundleTest {
+public:
+ GetInjectedBundleInitializationUserDataCallbackTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void initialize(WKBundleRef bundle, WKTypeRef userData)
+ {
+ WKBundlePostMessage(bundle, Util::toWK("Return the userData").get(), userData);
+ }
+};
+
+static InjectedBundleTest::Register<GetInjectedBundleInitializationUserDataCallbackTest> registrar("GetInjectedBundleInitializationUserDataCallbackTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/HitTestResultNodeHandle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/HitTestResultNodeHandle.cpp
new file mode 100644
index 000000000..1f4a3726c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/HitTestResultNodeHandle.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool done;
+static bool messageReceived;
+static bool didFinishLoad;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ messageReceived = true;
+ if (WKStringIsEqualToUTF8CString(messageName, "HitTestResultNodeHandleTestDoneMessageName"))
+ done = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+static void setInjectedBundleClient(WKContextRef context)
+{
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+}
+
+TEST(WebKit2, HitTestResultNodeHandle)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("HitTestResultNodeHandleTest"));
+
+ setInjectedBundleClient(context.get());
+
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple", "html")).get());
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ webView.simulateRightClick(10, 10);
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/HitTestResultNodeHandle_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/HitTestResultNodeHandle_Bundle.cpp
new file mode 100644
index 000000000..d68ff2193
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/HitTestResultNodeHandle_Bundle.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include "InjectedBundleController.h"
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundleHitTestResult.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+class HitTestResultNodeHandleTest : public InjectedBundleTest {
+public:
+ HitTestResultNodeHandleTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ static void getContextMenuFromDefaultMenu(WKBundlePageRef page, WKBundleHitTestResultRef hitTestResult, WKArrayRef defaultMenu, WKArrayRef* newMenu, WKTypeRef* userData, const void* clientInfo)
+ {
+ WKRetainPtr<WKBundleNodeHandleRef> nodeHandle(AdoptWK, WKBundleHitTestResultCopyNodeHandle(hitTestResult));
+ if (!nodeHandle)
+ return;
+
+ WKBundlePostMessage(InjectedBundleController::singleton().bundle(), Util::toWK("HitTestResultNodeHandleTestDoneMessageName").get(), Util::toWK("HitTestResultNodeHandleTestDoneMessageBody").get());
+ }
+
+ virtual void didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+ {
+ WKBundlePageContextMenuClientV0 contextMenuClient;
+ memset(&contextMenuClient, 0, sizeof(contextMenuClient));
+
+ contextMenuClient.base.version = 0;
+ contextMenuClient.getContextMenuFromDefaultMenu = getContextMenuFromDefaultMenu;
+
+ WKBundlePageSetContextMenuClient(page, &contextMenuClient.base);
+ }
+};
+
+static InjectedBundleTest::Register<HitTestResultNodeHandleTest> registrar("HitTestResultNodeHandleTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleBasic.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleBasic.cpp
new file mode 100644
index 000000000..a1d4046ed
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleBasic.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool done;
+static bool loadDone;
+static bool messageReceived;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ messageReceived = true;
+ if (loadDone)
+ done = true;
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ loadDone = true;
+ if (messageReceived)
+ done = true;
+}
+
+TEST(WebKit2, InjectedBundleBasic)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("InjectedBundleBasicTest"));
+
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context.get(), &injectedBundleClient.base);
+
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleBasic_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleBasic_Bundle.cpp
new file mode 100644
index 000000000..4d8689b93
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleBasic_Bundle.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+class InjectedBundleBasicTest : public InjectedBundleTest {
+public:
+ InjectedBundleBasicTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+ {
+ WKRetainPtr<WKStringRef> doneMessageName = adoptWK(WKStringCreateWithUTF8CString("DoneMessageName"));
+ WKRetainPtr<WKStringRef> doneMessageBody = adoptWK(WKStringCreateWithUTF8CString("DoneMessageBody"));
+ WKBundlePostMessage(bundle, doneMessageName.get(), doneMessageBody.get());
+ }
+};
+
+static InjectedBundleTest::Register<InjectedBundleBasicTest> registrar("InjectedBundleBasicTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleFrameHitTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleFrameHitTest.cpp
new file mode 100644
index 000000000..e178dc3cb
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleFrameHitTest.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool done;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ if (WKStringIsEqualToUTF8CString(messageName, "InjectedBundleFrameHitTestDone")) {
+ done = true;
+ WKStringRef linkTitle = (WKStringRef)messageBody;
+ EXPECT_WK_STREQ("HitTestLinkTitle", linkTitle);
+ }
+}
+
+static void setInjectedBundleClient(WKContextRef context)
+{
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+}
+
+TEST(WebKit2, InjectedBundleFrameHitTest)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("InjectedBundleFrameHitTestTest"));
+
+ setInjectedBundleClient(context.get());
+
+ PlatformWebView webView(context.get());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("link-with-title", "html")).get());
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleFrameHitTest_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleFrameHitTest_Bundle.cpp
new file mode 100644
index 000000000..14806685e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleFrameHitTest_Bundle.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundle.h>
+#include <WebKit/WKBundleFramePrivate.h>
+#include <WebKit/WKBundleHitTestResult.h>
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+class InjectedBundleFrameHitTestTest : public InjectedBundleTest {
+public:
+ InjectedBundleFrameHitTestTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef) override;
+ void frameLoadFinished(WKBundleFrameRef);
+
+ WKBundleRef m_bundle;
+};
+
+static InjectedBundleTest::Register<InjectedBundleFrameHitTestTest> registrar("InjectedBundleFrameHitTestTest");
+
+static void didFinishLoadForFrameCallback(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo)
+{
+ ((InjectedBundleFrameHitTestTest*)clientInfo)->frameLoadFinished(frame);
+}
+
+void InjectedBundleFrameHitTestTest::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+{
+ m_bundle = bundle;
+
+ WKBundlePageLoaderClientV1 pageLoaderClient;
+ memset(&pageLoaderClient, 0, sizeof(pageLoaderClient));
+
+ pageLoaderClient.base.version = 1;
+ pageLoaderClient.base.clientInfo = this;
+ pageLoaderClient.didFinishLoadForFrame = didFinishLoadForFrameCallback;
+
+ WKBundlePageSetPageLoaderClient(page, &pageLoaderClient.base);
+}
+
+void InjectedBundleFrameHitTestTest::frameLoadFinished(WKBundleFrameRef frame)
+{
+ WKBundleHitTestResultRef hitTestResult = WKBundleFrameCreateHitTestResult(frame, WKPointMake(50, 50));
+ WKRetainPtr<WKStringRef> linkTitle(AdoptWK, WKBundleHitTestResultCopyLinkTitle(hitTestResult));
+ WKBundlePostMessage(m_bundle, Util::toWK("InjectedBundleFrameHitTestDone").get(), linkTitle.get());
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleInitializationUserDataCallbackWins.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleInitializationUserDataCallbackWins.cpp
new file mode 100644
index 000000000..678aaa85b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleInitializationUserDataCallbackWins.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool done;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef, WKTypeRef messageBody, const void* clientInfo)
+{
+ EXPECT_WK_STREQ("Set in the getInjectedBundleInitializationUserData callback", static_cast<WKStringRef>(messageBody));
+ done = true;
+}
+
+static WKTypeRef getInjectedBundleInitializationUserData(WKContextRef context, const void* clientInfo)
+{
+ return Util::createInitializationDictionaryForInjectedBundleTest("InjectedBundleInitializationUserDataCallbackWinsTest", Util::toWK("Set in the getInjectedBundleInitializationUserData callback").get());
+}
+
+TEST(WebKit2, InjectedBundleInitializationUserDataCallbackWins)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextWithInjectedBundle());
+ WKRetainPtr<WKDictionaryRef> initializationDictionary(AdoptWK, Util::createInitializationDictionaryForInjectedBundleTest("InjectedBundleInitializationUserDataCallbackWinsTest", Util::toWK("Set with WKContextSetInitializationUserDataForInjectedBundle").get()));
+ WKContextSetInitializationUserDataForInjectedBundle(context.get(), initializationDictionary.get());
+
+ WKContextInjectedBundleClientV1 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 1;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+ injectedBundleClient.getInjectedBundleInitializationUserData = getInjectedBundleInitializationUserData;
+
+ WKContextSetInjectedBundleClient(context.get(), &injectedBundleClient.base);
+
+ PlatformWebView webView(context.get());
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleInitializationUserDataCallbackWins_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleInitializationUserDataCallbackWins_Bundle.cpp
new file mode 100644
index 000000000..cac7738dd
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/InjectedBundleInitializationUserDataCallbackWins_Bundle.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include "PlatformUtilities.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+class InjectedBundleInitializationUserDataCallbackWinsTest : public InjectedBundleTest {
+public:
+ InjectedBundleInitializationUserDataCallbackWinsTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void initialize(WKBundleRef bundle, WKTypeRef userData)
+ {
+ WKBundlePostMessage(bundle, Util::toWK("Return the userData").get(), userData);
+ }
+};
+
+static InjectedBundleTest::Register<InjectedBundleInitializationUserDataCallbackWinsTest> registrar("InjectedBundleInitializationUserDataCallbackWinsTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/LayoutMilestonesWithAllContentInFrame.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/LayoutMilestonesWithAllContentInFrame.cpp
new file mode 100644
index 000000000..aa294643c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/LayoutMilestonesWithAllContentInFrame.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKContextPrivate.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool testDone;
+
+static void didLayout(WKPageRef page, WKLayoutMilestones milestones, WKTypeRef, const void* clientInfo)
+{
+ // This test ensures that the DidFirstVisuallyNonEmptyLayout will be reached for the main frame
+ // even when all of the content is in a subframe.
+ if (milestones & kWKDidFirstVisuallyNonEmptyLayout)
+ testDone = true;
+}
+
+TEST(WebKit2, LayoutMilestonesWithAllContentInFrame)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV3 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 3;
+ loaderClient.base.clientInfo = &webView;
+ loaderClient.didLayout = didLayout;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPageListenForLayoutMilestones(webView.page(), kWKDidFirstVisuallyNonEmptyLayout);
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("all-content-in-one-iframe", "html")).get());
+
+ Util::run(&testDone);
+ EXPECT_TRUE(testDone);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/LoadAlternateHTMLStringWithNonDirectoryURL.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/LoadAlternateHTMLStringWithNonDirectoryURL.cpp
new file mode 100644
index 000000000..acc23348b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/LoadAlternateHTMLStringWithNonDirectoryURL.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "JavaScriptTest.h"
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+#include <WebKit/WKContext.h>
+#include <WebKit/WKPage.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool didFinishLoad = false;
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ didFinishLoad = true;
+}
+
+static void loadAlternateHTMLString(WKURLRef baseURL, WKURLRef unreachableURL)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKStringRef> alternateHTMLString(AdoptWK, WKStringCreateWithUTF8CString("<html><body><img src='icon.png'></body></html>"));
+ WKPageLoadAlternateHTMLString(webView.page(), alternateHTMLString.get(), baseURL, unreachableURL);
+
+ // If we can finish loading the html without resulting in an invalid message being sent from the WebProcess, this test passes.
+ Util::run(&didFinishLoad);
+}
+
+TEST(WebKit2, LoadAlternateHTMLStringWithNonDirectoryURL)
+{
+ // Call WKPageLoadAlternateHTMLString() with fileURL which does not point to a directory.
+ WKRetainPtr<WKURLRef> fileURL(AdoptWK, Util::createURLForResource("simple", "html"));
+ loadAlternateHTMLString(fileURL.get(), fileURL.get());
+}
+
+TEST(WebKit2, LoadAlternateHTMLStringWithEmptyBaseURL)
+{
+ // Call WKPageLoadAlternateHTMLString() with empty baseURL to make sure this test works
+ // when baseURL does not grant read access to the unreachableURL. We use a separate test
+ // to ensure the previous test does not pollute the result.
+ WKRetainPtr<WKURLRef> unreachableURL(AdoptWK, Util::URLForNonExistentResource());
+ loadAlternateHTMLString(nullptr, unreachableURL.get());
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/LoadCanceledNoServerRedirectCallback.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/LoadCanceledNoServerRedirectCallback.cpp
new file mode 100644
index 000000000..ac45fba26
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/LoadCanceledNoServerRedirectCallback.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "JavaScriptTest.h"
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+#include <WebKit/WKContext.h>
+#include <WebKit/WKFrame.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool loadedMainFrame;
+static bool loadedIFrame;
+static bool loadedAllFrames;
+
+static bool performedServerRedirect;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef frame, WKTypeRef, const void*)
+{
+ if (WKFrameIsMainFrame(frame))
+ loadedMainFrame = true;
+ else
+ loadedIFrame = true;
+
+ loadedAllFrames = loadedMainFrame && loadedIFrame;
+}
+
+static void didPerformServerRedirect(WKContextRef context, WKPageRef page, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void *clientInfo)
+{
+ performedServerRedirect = true;
+}
+
+TEST(WebKit2, LoadCanceledNoServerRedirectCallback)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("LoadCanceledNoServerRedirectCallbackTest"));
+
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+
+ WKContextSetInjectedBundleClient(context.get(), &injectedBundleClient.base);
+
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKContextHistoryClientV0 historyClient;
+ memset(&historyClient, 0, sizeof(historyClient));
+
+ historyClient.base.version = 0;
+ historyClient.didPerformServerRedirect = didPerformServerRedirect;
+
+ WKContextSetHistoryClient(context.get(), &historyClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple-iframe", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+ Util::run(&loadedAllFrames);
+
+ // We shouldn't have performed a server redirect when the iframe load was cancelled.
+ EXPECT_FALSE(performedServerRedirect);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/LoadCanceledNoServerRedirectCallback_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/LoadCanceledNoServerRedirectCallback_Bundle.cpp
new file mode 100644
index 000000000..6ea0dc77d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/LoadCanceledNoServerRedirectCallback_Bundle.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include "PlatformUtilities.h"
+#include "Test.h"
+
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundleFrame.h>
+#include <WebKit/WKRetainPtr.h>
+
+#include <wtf/Assertions.h>
+
+namespace TestWebKitAPI {
+
+class LoadCanceledNoServerRedirectCallbackTest : public InjectedBundleTest {
+public:
+ LoadCanceledNoServerRedirectCallbackTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ static WKURLRequestRef willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void *clientInfo)
+ {
+ // Allow the loading of the main resource, but don't allow the loading of an iframe, return null from willSendRequest.
+ if (WKBundleFrameIsMainFrame(frame)) {
+ WKRetainPtr<WKURLRequestRef> newRequest = request;
+ return newRequest.leakRef();
+ }
+
+ return 0;
+ }
+
+ virtual void didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+ {
+ WKBundlePageResourceLoadClientV0 resourceLoadClient;
+ memset(&resourceLoadClient, 0, sizeof(resourceLoadClient));
+
+ resourceLoadClient.base.version = 0;
+ resourceLoadClient.willSendRequestForFrame = willSendRequestForFrame;
+
+ WKBundlePageSetResourceLoadClient(page, &resourceLoadClient.base);
+
+ }
+};
+
+static InjectedBundleTest::Register<LoadCanceledNoServerRedirectCallbackTest> registrar("LoadCanceledNoServerRedirectCallbackTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/LoadPageOnCrash.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/LoadPageOnCrash.cpp
new file mode 100644
index 000000000..63ad664e2
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/LoadPageOnCrash.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013 Adenilson Cavalcanti <cavalcantii@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static void didFinishLoad(WKPageRef, WKFrameRef, WKTypeRef, const void*);
+
+class WebKit2CrashLoader {
+public:
+ WebKit2CrashLoader()
+ : context(AdoptWK, WKContextCreate())
+ , webView(context.get())
+ , url(adoptWK(WKURLCreateWithUTF8CString("about:blank")))
+ , firstSuccessfulLoad(false)
+ , secondSuccessfulLoad(false)
+ {
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.base.clientInfo = this;
+ loaderClient.didFinishLoadForFrame = didFinishLoad;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+ }
+
+ void loadUrl()
+ {
+ WKPageLoadURL(webView.page(), url.get());
+ }
+
+ void terminateWebProcess()
+ {
+ WKPageTerminate(webView.page());
+ }
+
+ WKRetainPtr<WKContextRef> context;
+ WKPageLoaderClientV0 loaderClient;
+ PlatformWebView webView;
+ WKRetainPtr<WKURLRef> url;
+
+ bool firstSuccessfulLoad;
+ bool secondSuccessfulLoad;
+};
+
+// We are going to have 2 load events intertwined by a simulated crash
+// (i.e. Load -> Crash -> Load).
+void didFinishLoad(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ WebKit2CrashLoader* testHelper = const_cast<WebKit2CrashLoader*>(static_cast<const WebKit2CrashLoader*>(clientInfo));
+
+ // First load worked, let's crash WebProcess.
+ if (!testHelper->firstSuccessfulLoad) {
+ testHelper->firstSuccessfulLoad = true;
+ return;
+ }
+
+ // Second load worked, we are done.
+ EXPECT_TRUE(testHelper->firstSuccessfulLoad);
+ if (!testHelper->secondSuccessfulLoad) {
+ testHelper->secondSuccessfulLoad = true;
+ return;
+ }
+}
+
+// This test will load a blank page and next kill WebProcess, the expected
+// result is that a call to page load should spawn a new WebProcess.
+TEST(WebKit2, LoadPageAfterCrash)
+{
+ WebKit2CrashLoader helper;
+ helper.loadUrl();
+ Util::run(&helper.firstSuccessfulLoad);
+ helper.terminateWebProcess();
+ helper.loadUrl();
+ Util::run(&helper.secondSuccessfulLoad);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/MenuTypesForMouseEvents.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/MenuTypesForMouseEvents.cpp
new file mode 100644
index 000000000..f77a11ad8
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/MenuTypesForMouseEvents.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "JavaScriptTest.h"
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+namespace TestWebKitAPI {
+
+static bool didFinishLoad;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+static void buildAndPerformTest(WKEventMouseButton button, WKEventModifiers modifiers, const char* expectedButton, const char* expectedMenuType)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("mouse-button-listener", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+ Util::run(&didFinishLoad);
+
+ didFinishLoad = false;
+
+ webView.simulateButtonClick(button, 10, 10, modifiers);
+
+ EXPECT_JS_EQ(webView.page(), "pressedMouseButton()", expectedButton);
+ EXPECT_JS_EQ(webView.page(), "displayedMenu()", expectedMenuType);
+}
+
+TEST(WebKit2, MenuAndButtonForNormalLeftClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonLeftButton, 0, "0", "none");
+}
+
+TEST(WebKit2, MenuAndButtonForNormalRightClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonRightButton, 0, "2", "context");
+}
+
+TEST(WebKit2, MenuAndButtonForNormalMiddleClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonMiddleButton, 0, "1", "none");
+}
+
+TEST(WebKit2, MenuAndButtonForControlLeftClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonLeftButton, kWKEventModifiersControlKey, "0", "context");
+}
+
+TEST(WebKit2, MenuAndButtonForControlRightClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonRightButton, kWKEventModifiersControlKey, "2", "context");
+}
+
+TEST(WebKit2, MenuAndButtonForControlMiddleClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonMiddleButton, kWKEventModifiersControlKey, "1", "none");
+}
+
+TEST(WebKit2, MenuAndButtonForShiftLeftClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonLeftButton, kWKEventModifiersShiftKey, "0", "none");
+}
+
+TEST(WebKit2, MenuAndButtonForShiftRightClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonRightButton, kWKEventModifiersShiftKey, "2", "context");
+}
+
+TEST(WebKit2, MenuAndButtonForShiftMiddleClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonMiddleButton, kWKEventModifiersShiftKey, "1", "none");
+}
+
+TEST(WebKit2, MenuAndButtonForCommandLeftClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonLeftButton, kWKEventModifiersMetaKey, "0", "none");
+}
+
+TEST(WebKit2, MenuAndButtonForCommandRightClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonRightButton, kWKEventModifiersMetaKey, "2", "context");
+}
+
+TEST(WebKit2, MenuAndButtonForCommandMiddleClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonMiddleButton, kWKEventModifiersMetaKey, "1", "none");
+}
+
+TEST(WebKit2, MenuAndButtonForAltLeftClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonLeftButton, kWKEventModifiersAltKey, "0", "none");
+}
+
+TEST(WebKit2, MenuAndButtonForAltRightClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonRightButton, kWKEventModifiersAltKey, "2", "context");
+}
+
+TEST(WebKit2, MenuAndButtonForAltMiddleClick)
+{
+ buildAndPerformTest(kWKEventMouseButtonMiddleButton, kWKEventModifiersAltKey, "1", "none");
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ModalAlertsSPI.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ModalAlertsSPI.cpp
new file mode 100644
index 000000000..0278f005b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ModalAlertsSPI.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKFrame.h>
+#include <WebKit/WKRetainPtr.h>
+#include <WebKit/WKSecurityOriginRef.h>
+
+namespace TestWebKitAPI {
+
+static bool done;
+static unsigned dialogsSeen;
+static const unsigned dialogsExpected = 3;
+
+static void analyzeDialogArguments(WKPageRef page, WKFrameRef frame, WKSecurityOriginRef securityOrigin)
+{
+ EXPECT_EQ(page, WKFrameGetPage(frame));
+
+ WKRetainPtr<WKURLRef> url = adoptWK(WKFrameCopyURL(frame));
+ WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(url.get()));
+ EXPECT_WK_STREQ("about:blank", urlString.get());
+
+ WKRetainPtr<WKStringRef> protocol = adoptWK(WKSecurityOriginCopyProtocol(securityOrigin));
+ EXPECT_WK_STREQ("file", protocol.get());
+
+ WKRetainPtr<WKStringRef> host = adoptWK(WKSecurityOriginCopyHost(securityOrigin));
+ EXPECT_WK_STREQ("", host.get());
+
+ EXPECT_EQ(WKSecurityOriginGetPort(securityOrigin), 0);
+
+ if (++dialogsSeen == dialogsExpected)
+ done = true;
+}
+
+static void runJavaScriptAlert(WKPageRef page, WKStringRef, WKFrameRef frame, WKSecurityOriginRef securityOrigin, const void*)
+{
+ analyzeDialogArguments(page, frame, securityOrigin);
+}
+
+static bool runJavaScriptConfirm(WKPageRef page, WKStringRef, WKFrameRef frame, WKSecurityOriginRef securityOrigin, const void*)
+{
+ analyzeDialogArguments(page, frame, securityOrigin);
+ return false;
+}
+
+static WKStringRef runJavaScriptPrompt(WKPageRef page, WKStringRef, WKStringRef, WKFrameRef frame, WKSecurityOriginRef securityOrigin, const void*)
+{
+ analyzeDialogArguments(page, frame, securityOrigin);
+ return nullptr;
+}
+
+static std::unique_ptr<PlatformWebView> openedWebView;
+
+static WKPageRef createNewPage(WKPageRef page, WKURLRequestRef urlRequest, WKDictionaryRef features, WKEventModifiers modifiers, WKEventMouseButton mouseButton, const void *clientInfo)
+{
+ EXPECT_TRUE(openedWebView == nullptr);
+
+ openedWebView = std::make_unique<PlatformWebView>(page);
+
+ WKPageUIClientV5 uiClient;
+ memset(&uiClient, 0, sizeof(uiClient));
+
+ uiClient.base.version = 5;
+ uiClient.runJavaScriptAlert = runJavaScriptAlert;
+ uiClient.runJavaScriptConfirm = runJavaScriptConfirm;
+ uiClient.runJavaScriptPrompt = runJavaScriptPrompt;
+
+ WKPageSetPageUIClient(openedWebView->page(), &uiClient.base);
+
+ WKRetain(openedWebView->page());
+ return openedWebView->page();
+}
+
+TEST(WebKit2, ModalAlertsSPI)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageUIClientV5 uiClient;
+ memset(&uiClient, 0, sizeof(uiClient));
+
+ uiClient.base.version = 5;
+ uiClient.createNewPage = createNewPage;
+
+ WKPageSetPageUIClient(webView.page(), &uiClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("modal-alerts-in-new-about-blank-window", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/MouseMoveAfterCrash.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/MouseMoveAfterCrash.cpp
new file mode 100644
index 000000000..9d7f3997d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/MouseMoveAfterCrash.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "JavaScriptTest.h"
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+namespace TestWebKitAPI {
+
+static bool didFinishLoad;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+TEST(WebKit2, MouseMoveAfterCrash)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("MouseMoveAfterCrashTest"));
+
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("mouse-move-listener", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+ Util::run(&didFinishLoad);
+
+ didFinishLoad = false;
+
+ WKContextPostMessageToInjectedBundle(context.get(), Util::toWK("Pause").get(), 0);
+
+ webView.simulateSpacebarKeyPress();
+
+ // Move the mouse once we are hung.
+ webView.simulateMouseMove(10, 10);
+ webView.simulateMouseMove(20, 20);
+
+ // After moving the mouse (while the web process was hung on the Pause message), kill the web process. It is restarted by reloading the page.
+ WKPageTerminate(webView.page());
+ WKPageReload(webView.page());
+
+ // Wait until we load the page a second time.
+ Util::run(&didFinishLoad);
+
+ EXPECT_JS_FALSE(webView.page(), "didMoveMouse()");
+
+ // Once the page has reloaded, try moving the mouse to verify that we get mouse move events.
+ webView.simulateMouseMove(10, 10);
+ webView.simulateMouseMove(20, 20);
+
+ EXPECT_JS_TRUE(webView.page(), "didMoveMouse()");
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/MouseMoveAfterCrash_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/MouseMoveAfterCrash_Bundle.cpp
new file mode 100644
index 000000000..8d92f4209
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/MouseMoveAfterCrash_Bundle.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+
+namespace TestWebKitAPI {
+
+class MouseMoveAfterCrashTest : public InjectedBundleTest {
+public:
+ MouseMoveAfterCrashTest(const std::string& identifier);
+
+private:
+ virtual void didReceiveMessage(WKBundleRef, WKStringRef messageName, WKTypeRef messageBody);
+};
+
+static InjectedBundleTest::Register<MouseMoveAfterCrashTest> registrar("MouseMoveAfterCrashTest");
+
+MouseMoveAfterCrashTest::MouseMoveAfterCrashTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+{
+}
+
+void MouseMoveAfterCrashTest::didReceiveMessage(WKBundleRef bundle, WKStringRef messageName, WKTypeRef)
+{
+ if (!WKStringIsEqualToUTF8CString(messageName, "Pause"))
+ return;
+
+ Util::sleep(30);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayout.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayout.cpp
new file mode 100644
index 000000000..778d285ba
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayout.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKContextPrivate.h>
+
+namespace TestWebKitAPI {
+
+static bool didFirstLayoutAchieved;
+static bool didFirstVisuallyNonEmptyLayoutAchieved;
+static bool didHitRelevantRepaintedObjectsAreaThresholdAchieved;
+static bool didUnlockAllLayoutMilestones;
+
+static void didLayout(WKPageRef, WKLayoutMilestones type, WKTypeRef, const void *)
+{
+ switch (type) {
+ case kWKDidFirstLayout:
+ didFirstLayoutAchieved = true;
+ break;
+ case kWKDidFirstVisuallyNonEmptyLayout:
+ didFirstVisuallyNonEmptyLayoutAchieved = true;
+ break;
+ case kWKDidHitRelevantRepaintedObjectsAreaThreshold:
+ didHitRelevantRepaintedObjectsAreaThresholdAchieved = true;
+ break;
+ default:
+ break;
+ }
+
+ if (didFirstLayoutAchieved && didFirstVisuallyNonEmptyLayoutAchieved && didHitRelevantRepaintedObjectsAreaThresholdAchieved)
+ didUnlockAllLayoutMilestones = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV3 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 3;
+ loaderClient.didLayout = didLayout;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+// FIXME: This test has been broken since http://trac.webkit.org/changeset/115752 It's failing because
+// the frame load is completing before didLayout() manages to unlock the
+// kWKDidHitRelevantRepaintedObjectsAreaThreshold achievement. We probably need to fix this by making
+// this test have a long-running resource.
+TEST(WebKit2, DISABLED_NewFirstVisuallyNonEmptyLayout)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("NewFirstVisuallyNonEmptyLayoutTest"));
+
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ // This test is expected to succeed because lots-of-text.html is a large document and the relevant painted
+ // objects take up more than 10% of the view.
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("lots-of-text", "html")).get());
+
+ Util::run(&didUnlockAllLayoutMilestones);
+ EXPECT_TRUE(didUnlockAllLayoutMilestones);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFails.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFails.cpp
new file mode 100644
index 000000000..5cff318fa
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFails.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKContextPrivate.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool didHitRelevantRepaintedObjectsAreaThresholdAchieved;
+static bool test1Done;
+static bool test2Done;
+
+static void didForceRepaint(WKErrorRef error, void*)
+{
+ EXPECT_NULL(error);
+ test2Done = true;
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ test1Done = true;
+ WKPageForceRepaint(page, 0, didForceRepaint);
+}
+
+static void didLayout(WKPageRef, WKLayoutMilestones type, WKTypeRef, const void *)
+{
+ if (type == kWKDidHitRelevantRepaintedObjectsAreaThreshold)
+ didHitRelevantRepaintedObjectsAreaThresholdAchieved = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV3 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 3;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+ loaderClient.didLayout = didLayout;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+TEST(WebKit2, NewFirstVisuallyNonEmptyLayoutFails)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("NewFirstVisuallyNonEmptyLayoutFailsTest"));
+
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ // This test is expected to fail because simple.html is a small document and the relevant painted
+ // objects take up less than 10% of the view.
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple", "html")).get());
+
+ Util::run(&test1Done);
+ Util::run(&test2Done);
+
+ // By the time the forced repaint has finished, the counter would have been hit
+ // if it was sized reasonably for the page.
+ EXPECT_FALSE(didHitRelevantRepaintedObjectsAreaThresholdAchieved);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFails_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFails_Bundle.cpp
new file mode 100644
index 000000000..2700d66c8
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFails_Bundle.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundlePagePrivate.h>
+
+namespace TestWebKitAPI {
+
+class NewFirstVisuallyNonEmptyLayoutFailsTest : public InjectedBundleTest {
+public:
+ NewFirstVisuallyNonEmptyLayoutFailsTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+ {
+ WKBundlePageListenForLayoutMilestones(page, kWKDidFirstLayout | kWKDidFirstVisuallyNonEmptyLayout | kWKDidHitRelevantRepaintedObjectsAreaThreshold);
+ }
+
+};
+
+static InjectedBundleTest::Register<NewFirstVisuallyNonEmptyLayoutFailsTest> registrar("NewFirstVisuallyNonEmptyLayoutFailsTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutForImages.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutForImages.cpp
new file mode 100644
index 000000000..7bd1fd7b3
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutForImages.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKContextPrivate.h>
+
+namespace TestWebKitAPI {
+
+static bool didFirstLayoutAchieved;
+static bool didFirstVisuallyNonEmptyLayoutAchieved;
+static bool didHitRelevantRepaintedObjectsAreaThresholdAchieved;
+static bool didUnlockAllLayoutMilestones;
+
+static void didLayout(WKPageRef, WKLayoutMilestones type, WKTypeRef, const void *)
+{
+ switch (type) {
+ case kWKDidFirstLayout:
+ didFirstLayoutAchieved = true;
+ break;
+ case kWKDidFirstVisuallyNonEmptyLayout:
+ didFirstVisuallyNonEmptyLayoutAchieved = true;
+ break;
+ case kWKDidHitRelevantRepaintedObjectsAreaThreshold:
+ didHitRelevantRepaintedObjectsAreaThresholdAchieved = true;
+ break;
+ default:
+ break;
+ }
+
+ if (didFirstLayoutAchieved && didFirstVisuallyNonEmptyLayoutAchieved && didHitRelevantRepaintedObjectsAreaThresholdAchieved)
+ didUnlockAllLayoutMilestones = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV3 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 3;
+ loaderClient.didLayout = didLayout;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+// FIXME: This test has been broken since http://trac.webkit.org/changeset/115752 It's failing because
+// the frame load is completing before didLayout() manages to unlock the
+// kWKDidHitRelevantRepaintedObjectsAreaThreshold achievement. We probably need to fix this by making
+// this test have a long-running resource.
+TEST(WebKit2, DISABLED_NewFirstVisuallyNonEmptyLayoutForImages)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("NewFirstVisuallyNonEmptyLayoutForImagesTest"));
+
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ // This test is expected to succeed because lots-of-images.html is a large document and the relevant painted
+ // objects take up more than 10% of the view.
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("lots-of-images", "html")).get());
+
+ Util::run(&didUnlockAllLayoutMilestones);
+ EXPECT_TRUE(didUnlockAllLayoutMilestones);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutForImages_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutForImages_Bundle.cpp
new file mode 100644
index 000000000..d8644bb69
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutForImages_Bundle.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundlePagePrivate.h>
+
+namespace TestWebKitAPI {
+
+class NewFirstVisuallyNonEmptyLayoutForImagesTest : public InjectedBundleTest {
+public:
+ NewFirstVisuallyNonEmptyLayoutForImagesTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+ {
+ WKBundlePageListenForLayoutMilestones(page, kWKDidFirstLayout | kWKDidFirstVisuallyNonEmptyLayout | kWKDidHitRelevantRepaintedObjectsAreaThreshold);
+ }
+
+};
+
+static InjectedBundleTest::Register<NewFirstVisuallyNonEmptyLayoutForImagesTest> registrar("NewFirstVisuallyNonEmptyLayoutForImagesTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFrames.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFrames.cpp
new file mode 100644
index 000000000..c7b24680e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFrames.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKContextPrivate.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool didHitRelevantRepaintedObjectsAreaThresholdMoreThanOnce;
+static unsigned didHitRelevantRepaintedObjectsAreaThresholdCounter;
+static bool test1Done;
+static bool test2Done;
+
+static void didForceRepaint(WKErrorRef error, void*)
+{
+ EXPECT_NULL(error);
+ test2Done = true;
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ test1Done = true;
+ WKPageForceRepaint(page, 0, didForceRepaint);
+}
+
+static void didLayout(WKPageRef, WKLayoutMilestones type, WKTypeRef, const void *)
+{
+ if (type != kWKDidHitRelevantRepaintedObjectsAreaThreshold)
+ return;
+
+ ++didHitRelevantRepaintedObjectsAreaThresholdCounter;
+ if (didHitRelevantRepaintedObjectsAreaThresholdCounter > 1)
+ didHitRelevantRepaintedObjectsAreaThresholdMoreThanOnce = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV3 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 3;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+ loaderClient.didLayout = didLayout;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+TEST(WebKit2, NewFirstVisuallyNonEmptyLayoutFrames)
+{
+ didHitRelevantRepaintedObjectsAreaThresholdCounter = 0;
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("NewFirstVisuallyNonEmptyLayoutFramesTest"));
+
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("lots-of-iframes", "html")).get());
+
+ Util::run(&test1Done);
+ Util::run(&test2Done);
+
+ // By the time the forced repaint has finished, the counter would have been hit
+ // if it was sized reasonably for the page.
+ EXPECT_FALSE(didHitRelevantRepaintedObjectsAreaThresholdMoreThanOnce);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFrames_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFrames_Bundle.cpp
new file mode 100644
index 000000000..f66fc957d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayoutFrames_Bundle.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundlePagePrivate.h>
+
+namespace TestWebKitAPI {
+
+class NewFirstVisuallyNonEmptyLayoutFramesTest : public InjectedBundleTest {
+public:
+ NewFirstVisuallyNonEmptyLayoutFramesTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+ {
+ WKBundlePageListenForLayoutMilestones(page, kWKDidFirstLayout | kWKDidFirstVisuallyNonEmptyLayout | kWKDidHitRelevantRepaintedObjectsAreaThreshold);
+ }
+
+};
+
+static InjectedBundleTest::Register<NewFirstVisuallyNonEmptyLayoutFramesTest> registrar("NewFirstVisuallyNonEmptyLayoutFramesTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayout_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayout_Bundle.cpp
new file mode 100644
index 000000000..8f1901d10
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/NewFirstVisuallyNonEmptyLayout_Bundle.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundlePagePrivate.h>
+
+namespace TestWebKitAPI {
+
+class NewFirstVisuallyNonEmptyLayoutTest : public InjectedBundleTest {
+public:
+ NewFirstVisuallyNonEmptyLayoutTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+ {
+ WKBundlePageListenForLayoutMilestones(page, kWKDidFirstLayout | kWKDidFirstVisuallyNonEmptyLayout | kWKDidHitRelevantRepaintedObjectsAreaThreshold);
+ }
+
+};
+
+static InjectedBundleTest::Register<NewFirstVisuallyNonEmptyLayoutTest> registrar("NewFirstVisuallyNonEmptyLayoutTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/PageLoadBasic.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/PageLoadBasic.cpp
new file mode 100644
index 000000000..ad62a5b29
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/PageLoadBasic.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool test1Done;
+
+struct State {
+ State()
+ : didDecidePolicyForNavigationAction(false)
+ , didStartProvisionalLoadForFrame(false)
+ , didCommitLoadForFrame(false)
+ {
+ }
+
+ bool didDecidePolicyForNavigationAction;
+ bool didStartProvisionalLoadForFrame;
+ bool didCommitLoadForFrame;
+};
+
+static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ State* state = reinterpret_cast<State*>(const_cast<void*>(clientInfo));
+ EXPECT_TRUE(state->didDecidePolicyForNavigationAction);
+ EXPECT_FALSE(state->didCommitLoadForFrame);
+
+ // The commited URL should be null.
+ EXPECT_NULL(WKFrameCopyURL(frame));
+
+ EXPECT_FALSE(state->didStartProvisionalLoadForFrame);
+
+ state->didStartProvisionalLoadForFrame = true;
+}
+
+static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ State* state = reinterpret_cast<State*>(const_cast<void*>(clientInfo));
+ EXPECT_TRUE(state->didDecidePolicyForNavigationAction);
+ EXPECT_TRUE(state->didStartProvisionalLoadForFrame);
+
+ // The provisional URL should be null.
+ EXPECT_NULL(WKFrameCopyProvisionalURL(frame));
+
+ state->didCommitLoadForFrame = true;
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ State* state = reinterpret_cast<State*>(const_cast<void*>(clientInfo));
+ EXPECT_TRUE(state->didDecidePolicyForNavigationAction);
+ EXPECT_TRUE(state->didStartProvisionalLoadForFrame);
+ EXPECT_TRUE(state->didCommitLoadForFrame);
+
+ // The provisional URL should be null.
+ EXPECT_NULL(WKFrameCopyProvisionalURL(frame));
+
+ test1Done = true;
+}
+
+static void decidePolicyForNavigationAction(WKPageRef page, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKFrameRef originatingFrame, WKURLRequestRef request, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
+{
+ State* state = reinterpret_cast<State*>(const_cast<void*>(clientInfo));
+ EXPECT_FALSE(state->didStartProvisionalLoadForFrame);
+ EXPECT_FALSE(state->didCommitLoadForFrame);
+ EXPECT_TRUE(mouseButton == kWKEventMouseButtonNoButton);
+
+ state->didDecidePolicyForNavigationAction = true;
+
+ WKFramePolicyListenerUse(listener);
+}
+
+static void decidePolicyForNewWindowAction(WKPageRef page, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKURLRequestRef request, WKStringRef frameName, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
+{
+ EXPECT_TRUE(mouseButton == kWKEventMouseButtonNoButton);
+ WKFramePolicyListenerUse(listener);
+}
+
+static void decidePolicyForResponse(WKPageRef page, WKFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, bool canShowMIMEType, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
+{
+ WKFramePolicyListenerUse(listener);
+}
+
+// FIXME: http://webkit.org/b/127934 REGRESSION (r163037): WebKit2.PageLoadBasic API test failing on Mountain Lion
+TEST(WebKit2, PageLoadBasic)
+{
+ State state;
+
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.base.clientInfo = &state;
+ loaderClient.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
+ loaderClient.didCommitLoadForFrame = didCommitLoadForFrame;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPagePolicyClientV1 policyClient;
+ memset(&policyClient, 0, sizeof(policyClient));
+
+ policyClient.base.version = 1;
+ policyClient.base.clientInfo = &state;
+ policyClient.decidePolicyForNavigationAction = decidePolicyForNavigationAction;
+ policyClient.decidePolicyForNewWindowAction = decidePolicyForNewWindowAction;
+ policyClient.decidePolicyForResponse = decidePolicyForResponse;
+
+ WKPageSetPagePolicyClient(webView.page(), &policyClient.base);
+
+ // Before loading anything, the active url should be null
+ EXPECT_NULL(WKPageCopyActiveURL(webView.page()));
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ // But immediately after starting a load, the active url should reflect the request
+ WKRetainPtr<WKURLRef> activeUrl = adoptWK(WKPageCopyActiveURL(webView.page()));
+ ASSERT_NOT_NULL(activeUrl.get());
+ EXPECT_TRUE(WKURLIsEqual(activeUrl.get(), url.get()));
+
+ Util::run(&test1Done);
+}
+
+TEST(WebKit2, PageReload)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ // Reload test before url loading.
+ WKPageReload(webView.page());
+ WKPageReload(webView.page());
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ // Reload test after url loading.
+ WKPageReload(webView.page());
+
+ WKRetainPtr<WKURLRef> activeUrl = adoptWK(WKPageCopyActiveURL(webView.page()));
+ ASSERT_NOT_NULL(activeUrl.get());
+ EXPECT_TRUE(WKURLIsEqual(activeUrl.get(), url.get()));
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/PageLoadDidChangeLocationWithinPageForFrame.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/PageLoadDidChangeLocationWithinPageForFrame.cpp
new file mode 100644
index 000000000..46838db93
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/PageLoadDidChangeLocationWithinPageForFrame.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static void nullJavaScriptCallback(WKSerializedScriptValueRef, WKErrorRef error, void*)
+{
+}
+
+static bool didFinishLoad;
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static bool didPopStateWithinPage;
+static bool didChangeLocationWithinPage;
+static void didSameDocumentNavigationForFrame(WKPageRef, WKFrameRef, WKSameDocumentNavigationType type, WKTypeRef, const void*)
+{
+ if (!didPopStateWithinPage) {
+ EXPECT_EQ(static_cast<uint32_t>(kWKSameDocumentNavigationSessionStatePop), type);
+ EXPECT_FALSE(didChangeLocationWithinPage);
+ didPopStateWithinPage = true;
+ return;
+ }
+
+ EXPECT_EQ(static_cast<uint32_t>(kWKSameDocumentNavigationAnchorNavigation), type);
+ didChangeLocationWithinPage = true;
+}
+
+TEST(WebKit2, PageLoadDidChangeLocationWithinPageForFrame)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+ loaderClient.didSameDocumentNavigationForFrame = didSameDocumentNavigationForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("file-with-anchor", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+ Util::run(&didFinishLoad);
+
+ WKRetainPtr<WKURLRef> initialURL = adoptWK(WKFrameCopyURL(WKPageGetMainFrame(webView.page())));
+
+ WKPageRunJavaScriptInMainFrame(webView.page(), Util::toWK("clickLink()").get(), 0, nullJavaScriptCallback);
+ Util::run(&didChangeLocationWithinPage);
+
+ WKRetainPtr<WKURLRef> urlAfterAnchorClick = adoptWK(WKFrameCopyURL(WKPageGetMainFrame(webView.page())));
+
+ EXPECT_FALSE(WKURLIsEqual(initialURL.get(), urlAfterAnchorClick.get()));
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ParentFrame.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ParentFrame.cpp
new file mode 100644
index 000000000..7953608c8
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ParentFrame.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKContextPrivate.h>
+
+namespace TestWebKitAPI {
+
+static bool didReceiveMessage;
+static bool isParentFrameCheckSuccessful;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef body, const void*)
+{
+ didReceiveMessage = true;
+
+ EXPECT_WK_STREQ("DidCheckParentFrame", messageName);
+ EXPECT_EQ(WKBooleanGetTypeID(), WKGetTypeID(body));
+
+ isParentFrameCheckSuccessful = WKBooleanGetValue(static_cast<WKBooleanRef>(body));
+}
+
+static void setInjectedBundleClient(WKContextRef context)
+{
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+}
+
+TEST(WebKit2, ParentFrame)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("ParentFrameTest"));
+ setInjectedBundleClient(context.get());
+
+ PlatformWebView webView(context.get());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple-iframe", "html")).get());
+
+ Util::run(&didReceiveMessage);
+ EXPECT_TRUE(isParentFrameCheckSuccessful);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ParentFrame_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ParentFrame_Bundle.cpp
new file mode 100644
index 000000000..e1da9ee02
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ParentFrame_Bundle.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundleFrame.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static WKRetainPtr<WKBundleRef> testBundle;
+static WKRetainPtr<WKBundleFrameRef> childFrame;
+
+class ParentFrameTest : public InjectedBundleTest {
+public:
+ ParentFrameTest(const std::string& identifier);
+
+private:
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef);
+
+};
+
+static InjectedBundleTest::Register<ParentFrameTest> registrar("ParentFrameTest");
+
+ParentFrameTest::ParentFrameTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+{
+}
+
+static void didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo)
+{
+ if (!WKBundleFrameIsMainFrame(frame)) {
+ childFrame = frame;
+ return;
+ }
+
+ bool isParentFrameCheckSuccessful = childFrame ? WKBundleFrameGetParentFrame(childFrame.get()) == frame : false;
+ WKBundlePostMessage(testBundle.get(), Util::toWK("DidCheckParentFrame").get(), adoptWK(WKBooleanCreate(isParentFrameCheckSuccessful)).get());
+}
+
+void ParentFrameTest::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+{
+ testBundle = bundle;
+
+ WKBundlePageLoaderClientV1 pageLoaderClient;
+ memset(&pageLoaderClient, 0, sizeof(pageLoaderClient));
+
+ pageLoaderClient.base.version = 1;
+ pageLoaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKBundlePageSetPageLoaderClient(page, &pageLoaderClient.base);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/PasteboardNotifications_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/PasteboardNotifications_Bundle.cpp
new file mode 100644
index 000000000..8c1404e7a
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/PasteboardNotifications_Bundle.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKArray.h>
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundleBackForwardListItem.h>
+#include <WebKit/WKWebArchive.h>
+
+namespace TestWebKitAPI {
+
+class PasteboardNotificationsTest : public InjectedBundleTest {
+public:
+ PasteboardNotificationsTest(const std::string& identifier);
+
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef);
+};
+
+static InjectedBundleTest::Register<PasteboardNotificationsTest> registrar("PasteboardNotificationsTest");
+
+static void willWriteToPasteboard(WKBundlePageRef page, WKBundleRangeHandleRef range, const void*)
+{
+ if (!range)
+ WKBundlePostMessage(InjectedBundleController::singleton().bundle(), Util::toWK("PasteboardNotificationTestDoneMessageName").get(), Util::toWK("willWritetoPasteboardFail").get());
+}
+
+static void getPasteboardDataForRange(WKBundlePageRef, WKBundleRangeHandleRef range, WKArrayRef* pasteboardTypes, WKArrayRef* pasteboardData, const void*)
+{
+ WKTypeRef typeName = WKStringCreateWithUTF8CString("AnotherArchivePasteboardType");
+ *pasteboardTypes = WKArrayCreateAdoptingValues(&typeName, 1);
+ WKTypeRef typeData = WKWebArchiveCopyData(WKWebArchiveCreateFromRange(range));
+ *pasteboardData = WKArrayCreateAdoptingValues(&typeData, 1);
+}
+
+static void didWriteToPasteboard(WKBundlePageRef, const void*)
+{
+ WKBundlePostMessage(InjectedBundleController::singleton().bundle(), Util::toWK("PasteboardNotificationTestDoneMessageName").get(), Util::toWK("didWriteToPasteboard").get());
+}
+
+PasteboardNotificationsTest::PasteboardNotificationsTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+{
+}
+
+void PasteboardNotificationsTest::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+{
+ WKBundlePageEditorClientV1 pageEditorClient;
+ memset(&pageEditorClient, 0, sizeof(pageEditorClient));
+
+ pageEditorClient.base.version = 1;
+ pageEditorClient.base.clientInfo = this;
+ pageEditorClient.willWriteToPasteboard = willWriteToPasteboard;
+ pageEditorClient.getPasteboardDataForRange = getPasteboardDataForRange;
+ pageEditorClient.didWriteToPasteboard = didWriteToPasteboard;
+
+ WKBundlePageSetEditorClient(page, &pageEditorClient.base);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/PreventEmptyUserAgent.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/PreventEmptyUserAgent.cpp
new file mode 100644
index 000000000..7712b142c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/PreventEmptyUserAgent.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <JavaScriptCore/JSContextRef.h>
+#include <WebKit/WKRetainPtr.h>
+#include <WebKit/WKSerializedScriptValue.h>
+
+namespace TestWebKitAPI {
+
+static bool testDone;
+
+static void didRunJavaScript(WKSerializedScriptValueRef resultSerializedScriptValue, WKErrorRef error, void* context)
+{
+ EXPECT_EQ(reinterpret_cast<void*>(0x1234578), context);
+ EXPECT_NOT_NULL(resultSerializedScriptValue);
+
+ JSGlobalContextRef scriptContext = JSGlobalContextCreate(0);
+ JSValueRef scriptValue = WKSerializedScriptValueDeserialize(resultSerializedScriptValue, scriptContext, 0);
+ EXPECT_TRUE(JSValueIsString(scriptContext, scriptValue));
+
+ // Make sure that the result of navigator.userAgent isn't empty, even if we set the custom
+ // user agent to the empty string.
+ JSStringRef scriptString = JSValueToStringCopy(scriptContext, scriptValue, 0);
+ EXPECT_GT(JSStringGetLength(scriptString), 0u);
+
+ JSStringRelease(scriptString);
+ JSGlobalContextRelease(scriptContext);
+
+ testDone = true;
+}
+
+TEST(WebKit2, PreventEmptyUserAgent)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageSetCustomUserAgent(webView.page(), WKStringCreateWithUTF8CString(""));
+ WKRetainPtr<WKStringRef> javaScriptString(AdoptWK, WKStringCreateWithUTF8CString("navigator.userAgent"));
+ WKPageRunJavaScriptInMainFrame(webView.page(), javaScriptString.get(), reinterpret_cast<void*>(0x1234578), didRunJavaScript);
+
+ Util::run(&testDone);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/PrivateBrowsingPushStateNoHistoryCallback.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/PrivateBrowsingPushStateNoHistoryCallback.cpp
new file mode 100644
index 000000000..532c92373
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/PrivateBrowsingPushStateNoHistoryCallback.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool didNavigate;
+static bool didSameDocumentNavigation;
+
+static void didNavigateWithoutNavigationData(WKContextRef context, WKPageRef page, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
+{
+ // This should never be called when navigating in Private Browsing.
+ FAIL();
+}
+
+static void didNavigateWithNavigationData(WKContextRef context, WKPageRef page, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
+{
+ didNavigate = true;
+}
+
+static void didSameDocumentNavigationForFrame(WKPageRef page, WKFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef userData, const void *clientInfo)
+{
+ didSameDocumentNavigation = true;
+}
+
+TEST(WebKit2, PrivateBrowsingPushStateNoHistoryCallback)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ WKContextHistoryClientV0 historyClient;
+ memset(&historyClient, 0, sizeof(historyClient));
+
+ historyClient.base.version = 0;
+ historyClient.didNavigateWithNavigationData = didNavigateWithoutNavigationData;
+
+ WKContextSetHistoryClient(context.get(), &historyClient.base);
+
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 pageLoaderClient;
+ memset(&pageLoaderClient, 0, sizeof(pageLoaderClient));
+
+ pageLoaderClient.base.version = 0;
+ pageLoaderClient.didSameDocumentNavigationForFrame = didSameDocumentNavigationForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &pageLoaderClient.base);
+
+ WKRetainPtr<WKPreferencesRef> preferences(AdoptWK, WKPreferencesCreate());
+ WKPreferencesSetPrivateBrowsingEnabled(preferences.get(), true);
+
+ WKPageGroupRef pageGroup = WKPageGetPageGroup(webView.page());
+ WKPageGroupSetPreferences(pageGroup, preferences.get());
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("push-state", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&didSameDocumentNavigation);
+
+ WKPreferencesSetPrivateBrowsingEnabled(preferences.get(), false);
+
+ historyClient.didNavigateWithNavigationData = didNavigateWithNavigationData;
+ WKContextSetHistoryClient(context.get(), &historyClient.base);
+
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&didNavigate);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ReloadPageAfterCrash.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ReloadPageAfterCrash.cpp
new file mode 100644
index 000000000..190c85341
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ReloadPageAfterCrash.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 Adenilson Cavalcanti <cavalcantii@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool loadBeforeCrash = false;
+static bool loadAfterCrash = false;
+
+static void didFinishLoad(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ // First load before WebProcess was terminated.
+ if (!loadBeforeCrash) {
+ loadBeforeCrash = true;
+ return;
+ }
+
+ // Next load after WebProcess was terminated (hopefully
+ // it will be correctly re-spawned).
+ EXPECT_EQ(static_cast<uint32_t>(kWKFrameLoadStateFinished), WKFrameGetFrameLoadState(frame));
+ EXPECT_FALSE(loadAfterCrash);
+
+ // Set it, otherwise the loop will not end.
+ loadAfterCrash = true;
+}
+
+static void didCrash(WKPageRef page, const void*)
+{
+ // Test if first load actually worked.
+ EXPECT_TRUE(loadBeforeCrash);
+
+ // Reload should re-spawn webprocess.
+ WKPageReload(page);
+}
+
+TEST(WebKit2, ReloadPageAfterCrash)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoad;
+ loaderClient.processDidCrash = didCrash;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ // Load a blank page and next kills WebProcess.
+ WKPageLoadURL(webView.page(), url.get());
+ Util::run(&loadBeforeCrash);
+ WKPageTerminate(webView.page());
+
+ // Let's try load a page and see what happens.
+ WKPageLoadURL(webView.page(), url.get());
+ Util::run(&loadAfterCrash);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ResizeReversePaginatedWebView.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ResizeReversePaginatedWebView.cpp
new file mode 100644
index 000000000..f2e1ae17f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ResizeReversePaginatedWebView.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "JavaScriptTest.h"
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <JavaScriptCore/JSContextRef.h>
+#include <WebKit/WKContextPrivate.h>
+#include <WebKit/WKPagePrivate.h>
+#include <WebKit/WKSerializedScriptValue.h>
+
+namespace TestWebKitAPI {
+
+static bool testDone;
+
+static const unsigned pageLength = 100;
+static const unsigned pageGap = 100;
+static const unsigned expectedPageCount = 20;
+
+static void didLayout(WKPageRef page, WKLayoutMilestones milestones, WKTypeRef, const void* clientInfo)
+{
+ if (milestones & kWKDidFirstLayoutAfterSuppressedIncrementalRendering) {
+ PlatformWebView* webView = (PlatformWebView*)clientInfo;
+
+ unsigned pageCount = WKPageGetPageCount(page);
+ EXPECT_EQ(expectedPageCount, pageCount);
+
+ webView->resizeTo((pageLength * pageCount) + (pageGap * (pageCount - 1)), 500);
+ EXPECT_JS_EQ(page, "window.scrollX", "0");
+
+ testDone = true;
+ }
+}
+
+TEST(WebKit2, ResizeReversePaginatedWebView)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV3 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 3;
+ loaderClient.base.clientInfo = &webView;
+ loaderClient.didLayout = didLayout;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPageListenForLayoutMilestones(webView.page(), kWKDidFirstLayoutAfterSuppressedIncrementalRendering);
+
+ WKPageGroupRef pageGroup = WKPageGetPageGroup(webView.page());
+ WKPreferencesRef preferences = WKPageGroupGetPreferences(pageGroup);
+ WKPreferencesSetSuppressesIncrementalRendering(preferences, true);
+
+ WKPageSetPaginationMode(webView.page(), kWKPaginationModeRightToLeft);
+ WKPageSetPageLength(webView.page(), pageLength);
+ WKPageSetGapBetweenPages(webView.page(), pageGap);
+ WKPageSetPaginationBehavesLikeColumns(webView.page(), true);
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("lots-of-text-vertical-lr", "html")).get());
+
+ Util::run(&testDone);
+ EXPECT_TRUE(testDone);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ResizeWindowAfterCrash.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ResizeWindowAfterCrash.cpp
new file mode 100644
index 000000000..2dfc8b4c2
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ResizeWindowAfterCrash.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013 Adenilson Cavalcanti <cavalcantii@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+struct TestStatesData {
+ TestStatesData(WKContextRef context)
+ : webView(context)
+ , firstLoad(false)
+ , resizeAfterCrash(false)
+ {
+ }
+
+ PlatformWebView webView;
+ bool firstLoad;
+ bool resizeAfterCrash;
+};
+
+static void didFinishLoad(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ TestStatesData* states = const_cast<TestStatesData*>(static_cast<const TestStatesData*>(clientInfo));
+ if (!states->firstLoad) {
+ // Loading a blank page worked, next we will kill WebProcess.
+ states->firstLoad = true;
+ return;
+ }
+
+ EXPECT_FALSE(states->resizeAfterCrash);
+ states->resizeAfterCrash = true;
+}
+
+static void didCrash(WKPageRef page, const void* clientInfo)
+{
+ TestStatesData* states = const_cast<TestStatesData*>(static_cast<const TestStatesData*>(clientInfo));
+ EXPECT_TRUE(states->firstLoad);
+ // Resize should work after WebProcess was terminated, unless
+ // the port's View is accessing nulled WebProcess related data,
+ // which would cause a crash.
+ states->webView.resizeTo(100, 200);
+ states->webView.resizeTo(300, 400);
+
+ WKPageReload(page);
+}
+
+TEST(WebKit2, ResizeWindowAfterCrash)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ TestStatesData states(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.base.clientInfo = &states;
+ loaderClient.didFinishLoadForFrame = didFinishLoad;
+ loaderClient.processDidCrash = didCrash;
+
+ WKPageSetPageLoaderClient(states.webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ // Load a blank page and next kills WebProcess.
+ WKPageLoadURL(states.webView.page(), url.get());
+ Util::run(&states.firstLoad);
+ WKPageTerminate(states.webView.page());
+
+ // Let's try load a page and see what happens.
+ WKPageLoadURL(states.webView.page(), url.get());
+ Util::run(&states.resizeAfterCrash);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ResponsivenessTimerDoesntFireEarly.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ResponsivenessTimerDoesntFireEarly.cpp
new file mode 100644
index 000000000..a034637e3
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ResponsivenessTimerDoesntFireEarly.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+namespace TestWebKitAPI {
+
+static bool didFinishLoad;
+static bool didBecomeUnresponsive;
+static bool didBrieflyPause;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef, const void*)
+{
+ didBrieflyPause = true;
+ EXPECT_WK_STREQ("DidBrieflyPause", messageName);
+}
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void processDidBecomeUnresponsive(WKPageRef, const void*)
+{
+ didBecomeUnresponsive = true;
+}
+
+static void setInjectedBundleClient(WKContextRef context)
+{
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+ loaderClient.processDidBecomeUnresponsive = processDidBecomeUnresponsive;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+TEST(WebKit2, ResponsivenessTimerDoesntFireEarly)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("ResponsivenessTimerDoesntFireEarlyTest"));
+ setInjectedBundleClient(context.get());
+
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple", "html")).get());
+ Util::run(&didFinishLoad);
+
+ WKContextPostMessageToInjectedBundle(context.get(), Util::toWK("BrieflyPause").get(), 0);
+
+ // Pressing a key on the keyboard should start the responsiveness timer. Since the web process
+ // is going to pause before it receives this keypress, it should take a little while to respond
+ // (but not so long that the responsiveness timer fires).
+ webView.simulateSpacebarKeyPress();
+
+ Util::run(&didBrieflyPause);
+
+ EXPECT_FALSE(didBecomeUnresponsive);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ResponsivenessTimerDoesntFireEarly_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ResponsivenessTimerDoesntFireEarly_Bundle.cpp
new file mode 100644
index 000000000..ddd2ca158
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ResponsivenessTimerDoesntFireEarly_Bundle.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+
+namespace TestWebKitAPI {
+
+class ResponsivenessTimerDoesntFireEarlyTest : public InjectedBundleTest {
+public:
+ ResponsivenessTimerDoesntFireEarlyTest(const std::string& identifier);
+
+private:
+ virtual void didReceiveMessage(WKBundleRef, WKStringRef messageName, WKTypeRef messageBody);
+};
+
+static InjectedBundleTest::Register<ResponsivenessTimerDoesntFireEarlyTest> registrar("ResponsivenessTimerDoesntFireEarlyTest");
+
+ResponsivenessTimerDoesntFireEarlyTest::ResponsivenessTimerDoesntFireEarlyTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+{
+}
+
+void ResponsivenessTimerDoesntFireEarlyTest::didReceiveMessage(WKBundleRef bundle, WKStringRef messageName, WKTypeRef)
+{
+ if (!WKStringIsEqualToUTF8CString(messageName, "BrieflyPause"))
+ return;
+
+ // The responsiveness timer is a 3-second timer. Pausing for 0.5 seconds should not cause it to fire.
+ Util::sleep(0.5);
+
+ WKBundlePostMessage(bundle, Util::toWK("DidBrieflyPause").get(), 0);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/RestoreSessionStateContainingFormData.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/RestoreSessionStateContainingFormData.cpp
new file mode 100644
index 000000000..b16537e70
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/RestoreSessionStateContainingFormData.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "JavaScriptTest.h"
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKSessionStateRef.h>
+
+namespace TestWebKitAPI {
+
+static bool didFinishLoad;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+static WKRetainPtr<WKDataRef> createSessionStateDataContainingFormData(WKContextRef context)
+{
+ PlatformWebView webView(context);
+ setPageLoaderClient(webView.page());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple-form", "html")).get());
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ EXPECT_JS_EQ(webView.page(), "submitForm()", "undefined");
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ auto sessionState = adoptWK(static_cast<WKSessionStateRef>(WKPageCopySessionState(webView.page(), reinterpret_cast<void*>(1), nullptr)));
+ return adoptWK(WKSessionStateCopyData(sessionState.get()));
+}
+
+TEST(WebKit2, RestoreSessionStateContainingFormData)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ // FIXME: Once <rdar://problem/8708435> is fixed, we can move the creation of this
+ // PlatformWebView after the call to createSessionStaetContainingFormData. Until then, it must
+ // remain here to avoid a race condition between the UI and web processes.
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ WKRetainPtr<WKDataRef> data = createSessionStateDataContainingFormData(context.get());
+ EXPECT_NOT_NULL(data);
+
+ auto sessionState = adoptWK(WKSessionStateCreateFromData(data.get()));
+ WKPageRestoreFromSessionState(webView.page(), sessionState.get());
+
+ Util::run(&didFinishLoad);
+
+ EXPECT_TRUE(WKPageCanGoBack(webView.page()));
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ScrollPinningBehaviors.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ScrollPinningBehaviors.cpp
new file mode 100644
index 000000000..015571f5f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ScrollPinningBehaviors.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "JavaScriptTest.h"
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <JavaScriptCore/JSContextRef.h>
+#include <WebKit/WKContextPrivate.h>
+#include <WebKit/WKPagePrivate.h>
+#include <WebKit/WKPreferencesRefPrivate.h>
+#include <WebKit/WKSerializedScriptValue.h>
+
+namespace TestWebKitAPI {
+
+static bool testDone;
+
+static void didFinishDocumentLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
+{
+ WKPageSetScrollPinningBehavior(page, kWKScrollPinningBehaviorPinToBottom);
+
+ EXPECT_JS_EQ(page, "window.scrollY", "2434");
+
+ PlatformWebView* webView = (PlatformWebView*)clientInfo;
+ webView->resizeTo(800, 200);
+
+ EXPECT_JS_EQ(page, "window.scrollY", "2834");
+ EXPECT_JS_EQ(page, "window.scrollTo(0,0)", "undefined");
+ EXPECT_JS_EQ(page, "window.scrollY", "2834");
+
+ WKPageSetScrollPinningBehavior(page, kWKScrollPinningBehaviorPinToTop);
+
+ EXPECT_JS_EQ(page, "window.scrollY", "0");
+ EXPECT_JS_EQ(page, "window.scrollTo(0,200)", "undefined");
+ EXPECT_JS_EQ(page, "window.scrollY", "0");
+
+ WKPageSetScrollPinningBehavior(page, kWKScrollPinningBehaviorDoNotPin);
+
+ EXPECT_JS_EQ(page, "window.scrollY", "0");
+ EXPECT_JS_EQ(page, "window.scrollTo(0,200)", "undefined");
+ EXPECT_JS_EQ(page, "window.scrollY", "200");
+
+ testDone = true;
+}
+
+TEST(WebKit2, ScrollPinningBehaviors)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ // Turn off threaded scrolling; synchronously waiting for the main thread scroll position to
+ // update using WKPageForceRepaint would be better, but for some reason doesn't block until
+ // it's updated after the initial WKPageSetScrollPinningBehavior above.
+ WKRetainPtr<WKPageGroupRef> pageGroup(AdoptWK, WKPageGroupCreateWithIdentifier(Util::toWK("NoThreadedScrollingPageGroup").get()));
+ WKPreferencesRef preferences = WKPageGroupGetPreferences(pageGroup.get());
+ WKPreferencesSetThreadedScrollingEnabled(preferences, false);
+
+ PlatformWebView webView(context.get(), pageGroup.get());
+
+ WKPageLoaderClientV3 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 3;
+ loaderClient.base.clientInfo = &webView;
+ loaderClient.didFinishDocumentLoadForFrame = didFinishDocumentLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple-tall", "html")).get());
+
+ Util::run(&testDone);
+ EXPECT_TRUE(testDone);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/SeccompFilters.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/SeccompFilters.cpp
new file mode 100644
index 000000000..9ca6080d3
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/SeccompFilters.cpp
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <WebKit/SeccompBroker.h>
+#include <WebKit/SeccompFilters.h>
+#include <WebKit/SyscallPolicy.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+using namespace WebKit;
+
+namespace TestWebKitAPI {
+
+DEPRECATED_DEFINE_STATIC_LOCAL(String, rootDir, (ASCIILiteral("/")));
+DEPRECATED_DEFINE_STATIC_LOCAL(String, homeDir, (String(getenv("HOME"))));
+DEPRECATED_DEFINE_STATIC_LOCAL(String, usrDir, (ASCIILiteral("/usr")));
+DEPRECATED_DEFINE_STATIC_LOCAL(String, usrSbinDir, (ASCIILiteral("/usr/sbin")));
+DEPRECATED_DEFINE_STATIC_LOCAL(String, testDirRead, (ASCIILiteral("/tmp/WebKitSeccompFilters/testRead")));
+DEPRECATED_DEFINE_STATIC_LOCAL(String, testDirWrite, (ASCIILiteral("/tmp/WebKitSeccompFilters/testWrite")));
+DEPRECATED_DEFINE_STATIC_LOCAL(String, testDirReadAndWrite, (ASCIILiteral("/tmp/WebKitSeccompFilters/testReadAndWrite")));
+DEPRECATED_DEFINE_STATIC_LOCAL(String, testDirNotAllowed, (ASCIILiteral("/tmp/WebKitSeccompFilters/testNotAllowed")));
+DEPRECATED_DEFINE_STATIC_LOCAL(String, testFileNotAllowed, (testDirReadAndWrite + "/testFilePolicy"));
+DEPRECATED_DEFINE_STATIC_LOCAL(String, testFileReadAndWrite, (testDirNotAllowed + "/testFilePolicy"));
+
+static const mode_t defaultMode = S_IRUSR | S_IWUSR | S_IXUSR;
+
+class SeccompEnvironment : public testing::Environment {
+public:
+ virtual void SetUp()
+ {
+ ASSERT_TRUE(!homeDir.isEmpty());
+
+ mkdir("/tmp/WebKitSeccompFilters", defaultMode);
+ mkdir(testDirRead.utf8().data(), defaultMode);
+ mkdir(testDirWrite.utf8().data(), defaultMode);
+ mkdir(testDirReadAndWrite.utf8().data(), defaultMode);
+ mkdir(testDirNotAllowed.utf8().data(), defaultMode);
+
+ // Create a file for the Read only and NotAllowed directory before
+ // loading the filters.
+ String file = testDirRead + "/testFile";
+ int fd = open(file.utf8().data(), O_RDWR | O_CREAT, defaultMode);
+ ASSERT_NE(close(fd), -1);
+ file = testDirNotAllowed + "/testFile";
+ fd = open(file.utf8().data(), O_RDWR | O_CREAT, defaultMode);
+ ASSERT_NE(close(fd), -1);
+
+ // Create files for the file policy tests. File policies precedes the
+ // directory policy. In this case, we create a file with read and write
+ // policies inside a directory that is not allowed, and vice versa.
+ fd = open(testFileNotAllowed.utf8().data(), O_RDWR | O_CREAT, defaultMode);
+ ASSERT_NE(close(fd), -1);
+ fd = open(testFileReadAndWrite.utf8().data(), O_RDWR | O_CREAT, defaultMode);
+ ASSERT_NE(close(fd), -1);
+
+ SyscallPolicy policy;
+ policy.addDirectoryPermission(rootDir, SyscallPolicy::NotAllowed);
+ policy.addDirectoryPermission(usrDir, SyscallPolicy::Read);
+ policy.addDirectoryPermission(usrSbinDir, SyscallPolicy::NotAllowed);
+ policy.addDirectoryPermission(testDirRead, SyscallPolicy::Read);
+ policy.addDirectoryPermission(testDirWrite, SyscallPolicy::Write);
+ policy.addDirectoryPermission(testDirReadAndWrite, SyscallPolicy::ReadAndWrite);
+ policy.addDirectoryPermission(testDirNotAllowed, SyscallPolicy::NotAllowed);
+ policy.addFilePermission(testFileNotAllowed, SyscallPolicy::NotAllowed);
+ policy.addFilePermission(testFileReadAndWrite, SyscallPolicy::ReadAndWrite);
+
+ SeccompFilters seccompFilters(SeccompFilters::Allow);
+ seccompFilters.addRule("open", SeccompFilters::Trap);
+ seccompFilters.addRule("openat", SeccompFilters::Trap);
+ seccompFilters.addRule("creat", SeccompFilters::Trap);
+
+ SeccompBroker::launchProcess(&seccompFilters, policy);
+ seccompFilters.initialize();
+ }
+
+ virtual void TearDown()
+ {
+ // This will have to move to a separated process created before loading
+ // the filters when we put the rmdir/unlink policies in place.
+ unlink("/tmp/WebKitSeccompFilters/testNotAllowed/testFile");
+ unlink("/tmp/WebKitSeccompFilters/testNotAllowed/testFilePolicy");
+ unlink("/tmp/WebKitSeccompFilters/testReadAndWrite/testFile");
+ unlink("/tmp/WebKitSeccompFilters/testReadAndWrite/testFile2");
+ unlink("/tmp/WebKitSeccompFilters/testReadAndWrite/testFile3");
+ unlink("/tmp/WebKitSeccompFilters/testReadAndWrite/testFilePolicy");
+ unlink("/tmp/WebKitSeccompFilters/testWrite/testFile");
+ unlink("/tmp/WebKitSeccompFilters/testWrite/testFile2");
+ unlink("/tmp/WebKitSeccompFilters/testRead/testFile");
+ rmdir("/tmp/WebKitSeccompFilters/testNotAllowed");
+ rmdir("/tmp/WebKitSeccompFilters/testReadAndWrite");
+ rmdir("/tmp/WebKitSeccompFilters/testWrite");
+ rmdir("/tmp/WebKitSeccompFilters/testRead");
+ rmdir("/tmp/WebKitSeccompFilters");
+ }
+};
+
+::testing::Environment* const env = ::testing::AddGlobalTestEnvironment(new SeccompEnvironment);
+
+static void dummyHandler(int, siginfo_t*, void*)
+{
+}
+
+TEST(WebKit2, sigaction)
+{
+ // Setting a handler should be enough to break any subsequent test if
+ // not silently ignored by the sandbox.
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_sigaction = &dummyHandler;
+ action.sa_flags = SA_SIGINFO;
+
+ ASSERT_NE(sigaction(SIGSYS, &action, 0), -1);
+}
+
+TEST(WebKit2, sigprocmask)
+{
+ // We test here the mechanism installed to prevent SIGSYS to be blocked. Any
+ // attemp to add SIGSYS to the set of blocked signals will be silently
+ // ignored (but other signals will be blocked just fine).
+ sigset_t set, oldSet;
+ sigemptyset(&set);
+ sigaddset(&set, SIGSYS);
+ sigaddset(&set, SIGUSR1);
+
+ ASSERT_NE(sigprocmask(SIG_BLOCK, &set, 0), -1);
+ ASSERT_NE(sigprocmask(SIG_BLOCK, 0, &oldSet), -1);
+ ASSERT_FALSE(sigismember(&oldSet, SIGSYS)) << "SIGSYS should not be blocked.";
+ ASSERT_TRUE(sigismember(&oldSet, SIGUSR1)) << "Other signals should be blocked normally.";
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGSYS);
+ sigaddset(&set, SIGUSR2);
+
+ ASSERT_NE(sigprocmask(SIG_SETMASK, &set, &oldSet), -1);
+ ASSERT_NE(sigprocmask(SIG_SETMASK, 0, &set), -1);
+ ASSERT_FALSE(sigismember(&set, SIGSYS)) << "SIGSYS should not be blocked.";
+ ASSERT_TRUE(sigismember(&set, SIGUSR2)) << "Other signals should be blocked normally.";
+ ASSERT_FALSE(sigismember(&oldSet, SIGUSR2));
+
+ ASSERT_NE(sigprocmask(SIG_SETMASK, &oldSet, 0), -1) << "Should restore the old signal set just fine.";
+ ASSERT_NE(sigprocmask(SIG_SETMASK, 0, &set), -1);
+ ASSERT_FALSE(sigismember(&set, SIGUSR2)) << "The restored set doesn't have SIGUSR2.";
+}
+
+TEST(WebKit2, open)
+{
+ // Read only directory.
+ String file = testDirRead + "/testFile";
+ int fd = open(file.utf8().data(), O_RDWR);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open(file.utf8().data(), O_WRONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open(file.utf8().data(), O_RDONLY | O_CREAT, defaultMode);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ file = testDirRead + "/ThisFileDoesNotExist";
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_TRUE(fd == -1 && errno == ENOENT) << "Should return ENOENT when trying " \
+ "to open a file that does not exit and the permissions are OK.";
+
+ fd = open(file.utf8().data(), O_WRONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES) << "Should return EACCES when trying " \
+ "to open a file that does not exit and the permissions are not OK.";
+
+ // Write only directory.
+ file = testDirWrite + "/testFile";
+ fd = open(file.utf8().data(), O_WRONLY | O_CREAT, defaultMode);
+ ASSERT_NE(fd, -1);
+ close(fd);
+
+ fd = open(file.utf8().data(), O_RDWR);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open(file.utf8().data(), O_WRONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ // Read an write directory.
+ file = testDirReadAndWrite + "/testFile";
+ fd = open(file.utf8().data(), O_WRONLY | O_CREAT, defaultMode);
+ ASSERT_NE(fd, -1);
+ close(fd);
+
+ fd = open(file.utf8().data(), O_RDWR);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ fd = open(file.utf8().data(), O_WRONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ // NotAllowed directory.
+ file = testDirNotAllowed + "/testFile";
+ fd = open(file.utf8().data(), O_WRONLY | O_CREAT, defaultMode);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open(file.utf8().data(), O_RDWR);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open(file.utf8().data(), O_WRONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+
+ // The /usr directory here has read permissions, so it's subdirectories
+ // should resolve to the /usr permissions unless explicitly specified.
+ file = usrDir + "/bin/basename";
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_NE(fd, -1) << "Subdirectories should with no policy should " \
+ "inherit the parent's policies.";
+ close(fd);
+
+ file = usrSbinDir + "/adduser";
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES) << "This directory should have " \
+ "its own policy instead of the parent's.";
+
+ // Access to the rest of the files system is blocked and should
+ // never return anything else other than EACCES regardless if the
+ // file exists or not. The reason is because it will fallback to the
+ // policy of the Root directory, marked as NotAllowed.
+ file = homeDir + "/testFile";
+ fd = open(file.utf8().data(), O_RDWR | O_CREAT, defaultMode);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open("/etc/passwd", O_RDONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ file = testDirReadAndWrite + "/../../../etc/passwd";
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ file = testDirReadAndWrite + "/../../.." + testDirReadAndWrite + "/../../../etc/passwd";
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ // Here we test file policies. The have precedence over directory policies.
+ // The file bellow lives inside a directory with ReadAndWrite policy.
+ fd = open(testFileNotAllowed.utf8().data(), O_RDONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open(testFileNotAllowed.utf8().data(), O_WRONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open(testFileNotAllowed.utf8().data(), O_RDWR);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ file = testDirReadAndWrite + "/../../.." + testDirReadAndWrite + "/testFilePolicy";
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ // The next file is located inside a directory marked as NotAllowed, but
+ // it has its own file policy that precedes the directory policy.
+ fd = open(testFileReadAndWrite.utf8().data(), O_RDONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ fd = open(testFileReadAndWrite.utf8().data(), O_WRONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ fd = open(testFileReadAndWrite.utf8().data(), O_RDWR);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ file = testDirReadAndWrite + "/../../.." + testDirNotAllowed + "/testFilePolicy";
+ fd = open(file.utf8().data(), O_RDONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+}
+
+TEST(WebKit2, creat)
+{
+ // Read only directory.
+ String file = testDirRead + "/testFile2";
+ int fd = creat(file.utf8().data(), defaultMode);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ // Write only directory.
+ file = testDirWrite + "/testFile2";
+ fd = creat(file.utf8().data(), defaultMode);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ // Read an write directory.
+ file = testDirReadAndWrite + "/testFile2";
+ fd = creat(file.utf8().data(), defaultMode);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ // NotAllowed directory.
+ file = testDirNotAllowed + "/testFile2";
+ fd = creat(file.utf8().data(), defaultMode);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+}
+
+TEST(WebKit2, openat)
+{
+ int dirFd = open(testDirReadAndWrite.utf8().data(), O_RDONLY);
+ ASSERT_NE(dirFd, -1);
+
+ int fd = openat(dirFd, "testFile3", O_RDWR | O_CREAT, defaultMode);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ fd = openat(dirFd, "testFile3", O_RDWR);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ fd = openat(dirFd, "testFile3", O_RDONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ fd = openat(dirFd, "testFile3", O_WRONLY);
+ EXPECT_NE(fd, -1);
+
+ fd = openat(fd, "testFile3", O_WRONLY);
+ EXPECT_TRUE(fd == -1 && errno == ENOTDIR) << "Should return ENOTDIR when the fd is a file.";
+ close(fd);
+
+ String file = "../../.." + testDirReadAndWrite + "/testFile3";
+ fd = openat(dirFd, file.utf8().data(), O_WRONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ file = "../../.." + testDirRead + "/testFile3";
+ fd = openat(dirFd, file.utf8().data(), O_WRONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ file = testDirReadAndWrite + "/testFile3";
+ fd = openat(-1, file.utf8().data(), O_WRONLY);
+ EXPECT_NE(fd, -1) << "Directory fd should be ignored when the path is absolute.";
+ close(fd);
+
+ fd = openat(-1, "testFile3", O_WRONLY);
+ EXPECT_TRUE(fd == -1 && errno == EBADF) << "Should return EBADF when the fd is invalid.";
+ close(dirFd);
+
+ dirFd = open(testDirNotAllowed.utf8().data(), O_RDONLY);
+ EXPECT_TRUE(dirFd == -1 && errno == EACCES);
+
+ dirFd = open(testDirRead.utf8().data(), O_RDONLY);
+ ASSERT_NE(dirFd, -1);
+
+ fd = openat(dirFd, "testFile2", O_RDONLY | O_CREAT, defaultMode);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = openat(dirFd, "testFile", O_WRONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+ close(dirFd);
+}
+
+static void* stressTest(void*)
+{
+ for (int i = 0; i < 500; ++i) {
+ int fd = open("/tmp/WebKitSeccompFilters/testRead/testFile", O_RDWR);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = open("/tmp/WebKitSeccompFilters/testRead/testFile", O_RDONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+
+ fd = open("/tmp/WebKitSeccompFilters/testNotAllowed/testFile", O_RDONLY);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ fd = creat("/tmp/WebKitSeccompFilters/testNotAllowed/SholdNotBeAllowed", defaultMode);
+ EXPECT_TRUE(fd == -1 && errno == EACCES);
+
+ int dirFd = open("/tmp/WebKitSeccompFilters/testRead", O_RDONLY);
+ EXPECT_NE(dirFd, -1);
+
+ fd = openat(dirFd, "testFile", O_RDONLY);
+ EXPECT_NE(fd, -1);
+ close(fd);
+ close(dirFd);
+ }
+
+ return 0;
+}
+
+TEST(WebKit2, threading)
+{
+ // Tests if concurrent syscall execution works fine. It can be
+ // also used for performance testing and leak detection. The test
+ // is disabled on Debug mode because it can be way too verbose.
+ pthread_t threads[5];
+
+ for (int i = 0; i < sizeof(threads) / sizeof(pthread_t); ++i)
+ pthread_create(&threads[i], 0, stressTest, 0);
+
+ for (int i = 0; i < sizeof(threads) / sizeof(pthread_t); ++i)
+ pthread_join(threads[i], 0);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ShouldGoToBackForwardListItem.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ShouldGoToBackForwardListItem.cpp
new file mode 100644
index 000000000..f41e44a5e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ShouldGoToBackForwardListItem.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+
+#include <WebKit/WKString.h>
+
+namespace TestWebKitAPI {
+
+static bool finished = false;
+static bool receivedProperBackForwardCallbacks = false;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef frame, WKTypeRef, const void*)
+{
+ // Only mark finished when the main frame loads
+ if (!WKFrameIsMainFrame(frame))
+ return;
+
+ finished = true;
+}
+
+static void willGoToBackForwardListItem(WKPageRef, WKBackForwardListItemRef, WKTypeRef userData, const void*)
+{
+ if (WKGetTypeID(userData) == WKStringGetTypeID()) {
+ if (WKStringIsEqualToUTF8CString((WKStringRef)userData, "shouldGoToBackForwardListItemCallback called as expected"))
+ receivedProperBackForwardCallbacks = true;
+ }
+
+ finished = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV1 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 1;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+ loaderClient.willGoToBackForwardListItem = willGoToBackForwardListItem;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+TEST(WebKit2, ShouldGoToBackForwardListItem)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("ShouldGoToBackForwardListItemTest"));
+ // Enable the page cache so we can test the WKBundleBackForwardListItemIsInPageCache API
+ WKContextSetCacheModel(context.get(), kWKCacheModelDocumentBrowser);
+
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple", "html")).get());
+ Util::run(&finished);
+
+ finished = false;
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple-iframe", "html")).get());
+ Util::run(&finished);
+
+ finished = false;
+ WKPageGoBack(webView.page());
+ Util::run(&finished);
+
+ EXPECT_EQ(receivedProperBackForwardCallbacks, true);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ShouldGoToBackForwardListItem_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ShouldGoToBackForwardListItem_Bundle.cpp
new file mode 100644
index 000000000..5eba75a9e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ShouldGoToBackForwardListItem_Bundle.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundleBackForwardListItem.h>
+
+namespace TestWebKitAPI {
+
+class ShouldGoToBackForwardListItemTest : public InjectedBundleTest {
+public:
+ ShouldGoToBackForwardListItemTest(const std::string& identifier);
+
+ virtual void didCreatePage(WKBundleRef bundle, WKBundlePageRef page);
+};
+
+static InjectedBundleTest::Register<ShouldGoToBackForwardListItemTest> registrar("ShouldGoToBackForwardListItemTest");
+
+static bool shouldGoToBackForwardListItemCallback(WKBundlePageRef, WKBundleBackForwardListItemRef item, WKTypeRef* userData, const void*)
+{
+ // The item should be in the page cache
+ if (WKBundleBackForwardListItemIsInPageCache(item))
+ *userData = WKStringCreateWithUTF8CString("shouldGoToBackForwardListItemCallback called as expected");
+
+ return true;
+}
+
+ShouldGoToBackForwardListItemTest::ShouldGoToBackForwardListItemTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+{
+}
+
+void ShouldGoToBackForwardListItemTest::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+{
+ WKBundlePageLoaderClientV1 pageLoaderClient;
+ memset(&pageLoaderClient, 0, sizeof(pageLoaderClient));
+
+ pageLoaderClient.base.version = 1;
+ pageLoaderClient.base.clientInfo = this;
+ pageLoaderClient.shouldGoToBackForwardListItem = shouldGoToBackForwardListItemCallback;
+
+ WKBundlePageSetPageLoaderClient(page, &pageLoaderClient.base);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/ShouldKeepCurrentBackForwardListItemInList.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/ShouldKeepCurrentBackForwardListItemInList.cpp
new file mode 100644
index 000000000..f8098e6f1
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/ShouldKeepCurrentBackForwardListItemInList.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+
+// This test navigates from simple.html, to simple2.html, to simple3.html
+// When navigating from simple2 to simple3, it disallows the simple2 back/forward list item from staying in the list
+// It then navigates back from simple3, expecting to land at simple.
+
+namespace TestWebKitAPI {
+
+static bool finished = false;
+static bool successfulSoFar = true;
+static int navigationNumber = 0;
+
+static bool itemURLLastComponentIsString(WKBackForwardListItemRef item, const char* string)
+{
+ WKRetainPtr<WKURLRef> url = adoptWK(WKBackForwardListItemCopyURL(item));
+ WKRetainPtr<WKStringRef> path = adoptWK(WKURLCopyLastPathComponent(url.get()));
+
+ return WKStringIsEqualToUTF8CString(path.get(), string);
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void*)
+{
+ // Only mark finished when the main frame loads
+ if (!WKFrameIsMainFrame(frame))
+ return;
+
+ finished = true;
+ navigationNumber++;
+
+ WKBackForwardListRef list = WKPageGetBackForwardList(page);
+ WKBackForwardListItemRef currentItem = WKBackForwardListGetCurrentItem(list);
+ WKBackForwardListItemRef backItem = WKBackForwardListGetBackItem(list);
+ WKBackForwardListItemRef forwardItem = WKBackForwardListGetForwardItem(list);
+ unsigned forwardCount = WKBackForwardListGetForwardListCount(list);
+
+ // This test should never have a forward list.
+ if (forwardCount)
+ successfulSoFar = false;
+
+ if (navigationNumber == 1) {
+ // We've only performed 1 navigation, we should only have a current item.
+ if (!currentItem || !itemURLLastComponentIsString(currentItem, "simple.html"))
+ successfulSoFar = false;
+ if (backItem || forwardItem)
+ successfulSoFar = false;
+ } else if (navigationNumber == 2) {
+ // On the second navigation, simple2 should be current and simple should be the back item.
+ if (!currentItem || !itemURLLastComponentIsString(currentItem, "simple2.html"))
+ successfulSoFar = false;
+ if (!backItem || !itemURLLastComponentIsString(backItem, "simple.html"))
+ successfulSoFar = false;
+ if (forwardItem)
+ successfulSoFar = false;
+ } else if (navigationNumber == 3) {
+ // On the third navigation the item for simple2 should have been removed.
+ // So simple3 should be current and simple should still be the back item.
+ if (!currentItem || !itemURLLastComponentIsString(currentItem, "simple3.html"))
+ successfulSoFar = false;
+ if (!backItem || !itemURLLastComponentIsString(backItem, "simple.html"))
+ successfulSoFar = false;
+ if (forwardItem)
+ successfulSoFar = false;
+ } else if (navigationNumber == 4) {
+ // After the fourth navigation (which was a "back" navigation), the item for simple3 should have been removed.
+ // So simple should be current and there should be no other items.
+ if (!currentItem || !itemURLLastComponentIsString(currentItem, "simple.html"))
+ successfulSoFar = false;
+ if (backItem || forwardItem)
+ successfulSoFar = false;
+ }
+}
+
+static void willGoToBackForwardListItem(WKPageRef, WKBackForwardListItemRef item, WKTypeRef userData, const void*)
+{
+ if (!itemURLLastComponentIsString(item, "simple.html"))
+ successfulSoFar = false;
+}
+
+static bool shouldKeepCurrentBackForwardListItemInList(WKPageRef page, WKBackForwardListItemRef item, const void*)
+{
+ // We make sure the item for "simple2.html" is removed when we navigate to "simple3.html"
+ // We also want to make sure the item for "simple3.html" is removed when we go back to "simple.html"
+ // So we only want to keep "simple.html"
+ return itemURLLastComponentIsString(item, "simple.html");
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV5 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 5;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+ loaderClient.shouldKeepCurrentBackForwardListItemInList = shouldKeepCurrentBackForwardListItemInList;
+ loaderClient.willGoToBackForwardListItem = willGoToBackForwardListItem;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+TEST(WebKit2, ShouldKeepCurrentBackForwardListItemInList)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple", "html")).get());
+ Util::run(&finished);
+ EXPECT_EQ(successfulSoFar, true);
+
+ finished = false;
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple2", "html")).get());
+ Util::run(&finished);
+ EXPECT_EQ(successfulSoFar, true);
+
+ finished = false;
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple3", "html")).get());
+ Util::run(&finished);
+ EXPECT_EQ(successfulSoFar, true);
+
+ finished = false;
+ WKPageGoBack(webView.page());
+ Util::run(&finished);
+
+ EXPECT_EQ(successfulSoFar, true);
+ EXPECT_EQ(navigationNumber, 4);
+}
+
+} // namespace TestWebKitAPI
+
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/SpacebarScrolling.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/SpacebarScrolling.cpp
new file mode 100644
index 000000000..226f52cc2
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/SpacebarScrolling.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "JavaScriptTest.h"
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <WebKit/WKRetainPtr.h>
+#include <WebKit/WKPreferencesPrivate.h>
+
+namespace TestWebKitAPI {
+
+static bool didFinishLoad;
+static bool didNotHandleKeyDownEvent;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void didNotHandleKeyEventCallback(WKPageRef, WKNativeEventPtr event, const void*)
+{
+ if (Util::isKeyDown(event))
+ didNotHandleKeyDownEvent = true;
+}
+
+TEST(WebKit2, SpacebarScrolling)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextWithInjectedBundle());
+
+ // Turn off threaded scrolling; synchronously waiting for the main thread scroll position to
+ // update using WKPageForceRepaint would be better, but for some reason the test still fails occasionally.
+ WKRetainPtr<WKPageGroupRef> pageGroup(AdoptWK, WKPageGroupCreateWithIdentifier(Util::toWK("NoThreadedScrollingPageGroup").get()));
+ WKPreferencesRef preferences = WKPageGroupGetPreferences(pageGroup.get());
+ WKPreferencesSetThreadedScrollingEnabled(preferences, false);
+
+ PlatformWebView webView(context.get(), pageGroup.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPageUIClientV0 uiClient;
+ memset(&uiClient, 0, sizeof(uiClient));
+
+ uiClient.base.version = 0;
+ uiClient.didNotHandleKeyEvent = didNotHandleKeyEventCallback;
+
+ WKPageSetPageUIClient(webView.page(), &uiClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("spacebar-scrolling", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+ Util::run(&didFinishLoad);
+
+ EXPECT_JS_FALSE(webView.page(), "isDocumentScrolled()");
+ EXPECT_JS_FALSE(webView.page(), "textFieldContainsSpace()");
+
+ webView.simulateSpacebarKeyPress();
+
+ EXPECT_JS_FALSE(webView.page(), "isDocumentScrolled()");
+ EXPECT_JS_TRUE(webView.page(), "textFieldContainsSpace()");
+
+ // On Mac, a key down event represents both a raw key down and a key press.
+ // We expect the key press to be handled (because it inserts text into the text field),
+ // but the raw key down should not be handled.
+#if PLATFORM(COCOA)
+ EXPECT_FALSE(didNotHandleKeyDownEvent);
+#endif
+
+ EXPECT_JS_EQ(webView.page(), "blurTextField()", "undefined");
+
+ didNotHandleKeyDownEvent = false;
+ webView.simulateSpacebarKeyPress();
+
+ EXPECT_JS_TRUE(webView.page(), "isDocumentScrolled()");
+ EXPECT_JS_TRUE(webView.page(), "textFieldContainsSpace()");
+
+#if PLATFORM(COCOA)
+ EXPECT_FALSE(didNotHandleKeyDownEvent);
+#endif
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/StopLoadingDuringDidFailProvisionalLoad.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/StopLoadingDuringDidFailProvisionalLoad.cpp
new file mode 100644
index 000000000..79656b627
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/StopLoadingDuringDidFailProvisionalLoad.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit2/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool done;
+static bool receivedMessageFromBundle;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ if (WKStringIsEqualToUTF8CString(messageName, "StopLoadingDuringDidFailProvisionalLoadTestDone"))
+ receivedMessageFromBundle = true;
+}
+
+static void setInjectedBundleClient(WKContextRef context)
+{
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+}
+
+static void didFailProvisionalLoadWithErrorForFrame(WKPageRef, WKFrameRef, WKErrorRef, WKTypeRef, const void*)
+{
+ // The injected bundle is notified of the failed load first. If we also receive this callback, the test didn't crash.
+ EXPECT_TRUE(receivedMessageFromBundle);
+ done = true;
+}
+
+TEST(WebKit2, StopLoadingDuringDidFailProvisionalLoadTest)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, Util::createContextForInjectedBundleTest("StopLoadingDuringDidFailProvisionalLoadTest"));
+ setInjectedBundleClient(context.get());
+
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+ loaderClient.didFailProvisionalLoadWithErrorForFrame = didFailProvisionalLoadWithErrorForFrame;
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::URLForNonExistentResource());
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/StopLoadingDuringDidFailProvisionalLoad_bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/StopLoadingDuringDidFailProvisionalLoad_bundle.cpp
new file mode 100644
index 000000000..bfb05fd79
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/StopLoadingDuringDidFailProvisionalLoad_bundle.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include "PlatformUtilities.h"
+#include <WebKit2/WKBundle.h>
+#include <WebKit2/WKBundleFramePrivate.h>
+#include <WebKit2/WKBundlePage.h>
+#include <WebKit2/WKBundlePagePrivate.h>
+
+namespace TestWebKitAPI {
+
+class StopLoadingDuringDidFailProvisionalLoadTest : public InjectedBundleTest {
+public:
+ StopLoadingDuringDidFailProvisionalLoadTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef) override;
+ void didFailProvisionalLoad(WKBundlePageRef, WKBundleFrameRef);
+
+ WKBundleRef m_bundle;
+};
+
+static InjectedBundleTest::Register<StopLoadingDuringDidFailProvisionalLoadTest> registrar("StopLoadingDuringDidFailProvisionalLoadTest");
+
+static void didFailProvisionalLoadWithErrorForFrameCallback(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef, WKTypeRef*, const void *clientInfo)
+{
+ ((StopLoadingDuringDidFailProvisionalLoadTest*)clientInfo)->didFailProvisionalLoad(page, frame);
+}
+
+void StopLoadingDuringDidFailProvisionalLoadTest::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+{
+ m_bundle = bundle;
+
+ WKBundlePageLoaderClientV2 pageLoaderClient;
+ memset(&pageLoaderClient, 0, sizeof(pageLoaderClient));
+
+ pageLoaderClient.base.version = 2;
+ pageLoaderClient.base.clientInfo = this;
+ pageLoaderClient.didFailProvisionalLoadWithErrorForFrame = didFailProvisionalLoadWithErrorForFrameCallback;
+
+ WKBundlePageSetPageLoaderClient(page, &pageLoaderClient.base);
+}
+
+void StopLoadingDuringDidFailProvisionalLoadTest::didFailProvisionalLoad(WKBundlePageRef page, WKBundleFrameRef)
+{
+ WKBundlePageStopLoading(page);
+ WKBundlePostMessage(m_bundle, Util::toWK("StopLoadingDuringDidFailProvisionalLoadTestDone").get(), nullptr);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/TerminateTwice.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/TerminateTwice.cpp
new file mode 100644
index 000000000..2e1950860
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/TerminateTwice.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+namespace TestWebKitAPI {
+
+// Disabled in debug mode while investigating <https://bugs.webkit.org/show_bug.cgi?id=136012>.
+#ifdef NDEBUG
+
+static bool loaded;
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
+{
+ loaded = true;
+}
+
+TEST(WebKit2, TerminateTwice)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKRetainPtr<WKURLRef> url(AdoptWK, Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&loaded);
+ WKPageTerminate(webView.page());
+
+ // Reloading will start relaunching the process.
+ WKPageReload(webView.page());
+
+ // And now we terminate the page before the process launch is complete.
+ WKPageTerminate(webView.page());
+}
+
+#endif
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/TextFieldDidBeginAndEndEditing.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/TextFieldDidBeginAndEndEditing.cpp
new file mode 100644
index 000000000..cfe0be254
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/TextFieldDidBeginAndEndEditing.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI && !PLATFORM(MAC)
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <wtf/StdLibExtras.h>
+
+namespace TestWebKitAPI {
+
+struct WebKit2TextFieldBeginAndEditEditingTest : public ::testing::Test {
+ std::unique_ptr<PlatformWebView> webView;
+
+ WKRetainPtr<WKStringRef> messageName;
+
+ bool didFinishLoad { false };
+ bool didReceiveMessage { false };
+
+ static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef, const void* clientInfo)
+ {
+ WebKit2TextFieldBeginAndEditEditingTest& client = *static_cast<WebKit2TextFieldBeginAndEditEditingTest*>(const_cast<void*>(clientInfo));
+ client.messageName = messageName;
+ client.didReceiveMessage = true;
+ }
+
+ static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void* clientInfo)
+ {
+ WebKit2TextFieldBeginAndEditEditingTest& client = *static_cast<WebKit2TextFieldBeginAndEditEditingTest*>(const_cast<void*>(clientInfo));
+ client.didFinishLoad = true;
+ }
+
+ static void setInjectedBundleClient(WKContextRef context, const void* clientInfo)
+ {
+ WKContextInjectedBundleClientV1 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 1;
+ injectedBundleClient.base.clientInfo = clientInfo;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+ }
+
+ static void setPageLoaderClient(WKPageRef page, const void* clientInfo)
+ {
+ WKPageLoaderClientV6 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 6;
+ loaderClient.base.clientInfo = clientInfo;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+ }
+
+ static void nullJavaScriptCallback(WKSerializedScriptValueRef, WKErrorRef, void*)
+ {
+ }
+
+ void executeJavaScriptAndCheckDidReceiveMessage(const char* javaScriptCode, const char* expectedMessageName)
+ {
+ didReceiveMessage = false;
+ WKPageRunJavaScriptInMainFrame(webView->page(), Util::toWK(javaScriptCode).get(), 0, nullJavaScriptCallback);
+ Util::run(&didReceiveMessage);
+ EXPECT_WK_STREQ(expectedMessageName, messageName);
+ }
+
+ // From ::testing::Test
+ void SetUp() override
+ {
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("TextFieldDidBeginAndEndEditingEventsTest"));
+ setInjectedBundleClient(context.get(), this);
+
+ webView = std::make_unique<PlatformWebView>(context.get());
+ setPageLoaderClient(webView->page(), this);
+
+ didFinishLoad = false;
+ didReceiveMessage = false;
+
+ WKPageLoadURL(webView->page(), adoptWK(Util::createURLForResource("input-focus-blur", "html")).get());
+ Util::run(&didFinishLoad);
+ }
+};
+
+TEST_F(WebKit2TextFieldBeginAndEditEditingTest, TextFieldDidBeginAndEndEditingEvents)
+{
+ executeJavaScriptAndCheckDidReceiveMessage("focusTextField('input')", "DidReceiveTextFieldDidBeginEditing");
+ executeJavaScriptAndCheckDidReceiveMessage("blurTextField('input')", "DidReceiveTextFieldDidEndEditing");
+}
+
+TEST_F(WebKit2TextFieldBeginAndEditEditingTest, TextFieldDidBeginAndEndEditingEventsInReadOnlyField)
+{
+ executeJavaScriptAndCheckDidReceiveMessage("focusTextField('readonly')", "DidReceiveTextFieldDidBeginEditing");
+ executeJavaScriptAndCheckDidReceiveMessage("blurTextField('readonly')", "DidReceiveTextFieldDidEndEditing");
+}
+
+TEST_F(WebKit2TextFieldBeginAndEditEditingTest, TextFieldDidBeginShouldNotBeDispatchedForAlreadyFocusedField)
+{
+ executeJavaScriptAndCheckDidReceiveMessage("focusTextField('input'); focusTextField('input')", "DidReceiveTextFieldDidBeginEditing");
+ executeJavaScriptAndCheckDidReceiveMessage("blurTextField('input')", "DidReceiveTextFieldDidEndEditing");
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/TextFieldDidBeginAndEndEditing_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/TextFieldDidBeginAndEndEditing_Bundle.cpp
new file mode 100644
index 000000000..19898f703
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/TextFieldDidBeginAndEndEditing_Bundle.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI && !PLATFORM(MAC)
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+
+namespace TestWebKitAPI {
+
+class TextFieldDidBeginAndEndEditingEventsTest : public InjectedBundleTest {
+public:
+ TextFieldDidBeginAndEndEditingEventsTest(const std::string& identifier);
+
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef);
+};
+
+static InjectedBundleTest::Register<TextFieldDidBeginAndEndEditingEventsTest> registrar("TextFieldDidBeginAndEndEditingEventsTest");
+
+static void textFieldDidBeginEditing(WKBundlePageRef, WKBundleNodeHandleRef inputElement, WKBundleFrameRef, const void*)
+{
+ WKBundlePostMessage(InjectedBundleController::singleton().bundle(), Util::toWK("DidReceiveTextFieldDidBeginEditing").get(), nullptr);
+}
+
+static void textFieldDidEndEditing(WKBundlePageRef, WKBundleNodeHandleRef inputElement, WKBundleFrameRef, const void*)
+{
+ WKBundlePostMessage(InjectedBundleController::singleton().bundle(), Util::toWK("DidReceiveTextFieldDidEndEditing").get(), nullptr);
+}
+
+TextFieldDidBeginAndEndEditingEventsTest::TextFieldDidBeginAndEndEditingEventsTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+{
+}
+
+void TextFieldDidBeginAndEndEditingEventsTest::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+{
+ WKBundlePageFormClientV2 formClient;
+ memset(&formClient, 0, sizeof(formClient));
+
+ formClient.base.version = 2;
+ formClient.base.clientInfo = this;
+ formClient.textFieldDidBeginEditing = textFieldDidBeginEditing;
+ formClient.textFieldDidEndEditing = textFieldDidEndEditing;
+
+ WKBundlePageSetFormClient(page, &formClient.base);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/UserMedia.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/UserMedia.cpp
new file mode 100644
index 000000000..230092cd4
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/UserMedia.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#if ENABLE(MEDIA_STREAM)
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <WebKit/WKRetainPtr.h>
+#include <string.h>
+#include <vector>
+
+namespace TestWebKitAPI {
+
+static bool done;
+
+void decidePolicyForUserMediaPermissionRequestCallBack(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKUserMediaPermissionRequestRef permissionRequest, const void* /* clientInfo */)
+{
+ WKUserMediaPermissionRequestAllow(permissionRequest);
+ done = true;
+}
+
+TEST(WebKit2, UserMediaBasic)
+{
+ auto context = adoptWK(WKContextCreate());
+ PlatformWebView webView(context.get());
+ WKPageUIClientV5 uiClient;
+ memset(&uiClient, 0, sizeof(uiClient));
+
+
+ uiClient.base.version = 5;
+ uiClient.decidePolicyForUserMediaPermissionRequest = decidePolicyForUserMediaPermissionRequestCallBack;
+
+ WKPageSetPageUIClient(webView.page(), &uiClient.base);
+
+ done = false;
+ auto url = adoptWK(Util::createURLForResource("getUserMedia", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif // ENABLE(MEDIA_STREAM)
+
+#endif // WK_HAVE_C_SPI
+
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/UserMessage.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/UserMessage.cpp
new file mode 100644
index 000000000..064cbb838
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/UserMessage.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "Test.h"
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+namespace TestWebKitAPI {
+
+class WebKit2UserMessageRoundTripTest : public ::testing::Test {
+public:
+ WebKit2UserMessageRoundTripTest()
+ : didFinishLoad(false)
+ , didReceiveMessage(false)
+ {
+ }
+
+ WKRetainPtr<WKContextRef> context;
+ std::unique_ptr<PlatformWebView> webView;
+
+ WKRetainPtr<WKTypeRef> recievedBody;
+
+ bool didFinishLoad;
+ bool didReceiveMessage;
+
+ static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+ {
+ if (!WKStringIsEqualToUTF8CString(messageName, "RoundTripReturn"))
+ return;
+
+ ((WebKit2UserMessageRoundTripTest*)clientInfo)->recievedBody = messageBody;
+ ((WebKit2UserMessageRoundTripTest*)clientInfo)->didReceiveMessage = true;
+ }
+
+ static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void* clientInfo)
+ {
+ ((WebKit2UserMessageRoundTripTest*)clientInfo)->didFinishLoad = true;
+ }
+
+ static void setInjectedBundleClient(WKContextRef context, const void* clientInfo)
+ {
+ WKContextInjectedBundleClientV1 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 1;
+ injectedBundleClient.base.clientInfo = clientInfo;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+ }
+
+ static void setPageLoaderClient(WKPageRef page, const void* clientInfo)
+ {
+ WKPageLoaderClientV3 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 3;
+ loaderClient.base.clientInfo = clientInfo;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+ }
+
+ virtual void SetUp()
+ {
+ context = adoptWK(Util::createContextForInjectedBundleTest("UserMessageTest"));
+ setInjectedBundleClient(context.get(), this);
+
+ webView = std::make_unique<PlatformWebView>(context.get());
+ setPageLoaderClient(webView->page(), this);
+
+ didFinishLoad = false;
+ didReceiveMessage = false;
+
+ // Force the creation of the
+ WKPageLoadURL(webView->page(), adoptWK(Util::createURLForResource("simple", "html")).get());
+ Util::run(&didFinishLoad);
+
+ }
+
+ // Used to test sending a WKType round trip to the WebProcess and back.
+ // Result is stored into the recievedBody member variable.
+ void roundTrip(WKTypeRef object)
+ {
+ WKTypeID storedTypeID = WKGetTypeID(object);
+
+ recievedBody.clear();
+ didReceiveMessage = false;
+ WKContextPostMessageToInjectedBundle(context.get(), Util::toWK("RoundTrip").get(), object);
+ Util::run(&didReceiveMessage);
+
+ EXPECT_NOT_NULL(recievedBody);
+ EXPECT_EQ(storedTypeID, WKGetTypeID(recievedBody.get()));
+ }
+};
+
+
+TEST_F(WebKit2UserMessageRoundTripTest, WKURLRequestRef)
+{
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLCreateWithUTF8CString("http://webkit.org/"));
+ WKRetainPtr<WKURLRequestRef> request = adoptWK(WKURLRequestCreateWithWKURL(url.get()));
+
+ roundTrip(request.get());
+ WKTypeRef roundTrippedTypeRef = recievedBody.get();
+
+ WKRetainPtr<WKURLRequestRef> roundTrippedRequest = static_cast<WKURLRequestRef>(roundTrippedTypeRef);
+ WKRetainPtr<WKURLRef> roundTrippedURL = adoptWK(WKURLRequestCopyURL(roundTrippedRequest.get()));
+ EXPECT_TRUE(WKURLIsEqual(roundTrippedURL.get(), url.get()));
+}
+
+TEST_F(WebKit2UserMessageRoundTripTest, WKURL)
+{
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLCreateWithUTF8CString("http://webkit.org/"));
+
+ roundTrip(url.get());
+ WKTypeRef roundTrippedTypeRef = recievedBody.get();
+
+ WKRetainPtr<WKURLRef> roundTrippedURL = static_cast<WKURLRef>(roundTrippedTypeRef);
+ EXPECT_TRUE(WKURLIsEqual(roundTrippedURL.get(), url.get()));
+}
+
+TEST_F(WebKit2UserMessageRoundTripTest, WKString)
+{
+ WKRetainPtr<WKStringRef> string = adoptWK(WKStringCreateWithUTF8CString("An important string"));
+
+ roundTrip(string.get());
+ WKTypeRef roundTrippedTypeRef = recievedBody.get();
+
+ WKRetainPtr<WKStringRef> roundTrippedString = static_cast<WKStringRef>(roundTrippedTypeRef);
+ EXPECT_TRUE(WKStringIsEqual(roundTrippedString.get(), string.get()));
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/UserMessage_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/UserMessage_Bundle.cpp
new file mode 100644
index 000000000..cec713655
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/UserMessage_Bundle.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+
+namespace TestWebKitAPI {
+
+class UserMessageTest : public InjectedBundleTest {
+public:
+ UserMessageTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+private:
+ virtual void didReceiveMessage(WKBundleRef bundle, WKStringRef messageName, WKTypeRef messageBody)
+ {
+ if (!WKStringIsEqualToUTF8CString(messageName, "RoundTrip"))
+ return;
+
+ WKBundlePostMessage(bundle, Util::toWK("RoundTripReturn").get(), messageBody);
+ }
+};
+
+static InjectedBundleTest::Register<UserMessageTest> registrar("UserMessageTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKBundleFileHandle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKBundleFileHandle.cpp
new file mode 100644
index 000000000..b9a84ff80
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKBundleFileHandle.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+
+namespace TestWebKitAPI {
+
+static bool done;
+static bool loadDone;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef body, const void*)
+{
+ if (!WKStringIsEqualToUTF8CString(messageName, "SUCCESS"))
+ FAIL();
+ else
+ SUCCEED();
+
+ done = true;
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void*)
+{
+ loadDone = true;
+}
+
+TEST(WebKit2, WKBundleFileHandle)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("WKBundleFileHandleTest"));
+
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+ WKContextSetInjectedBundleClient(context.get(), &injectedBundleClient.base);
+
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("bundle-file", "html")).get());
+ Util::run(&loadDone);
+
+ // Get path to a file.
+ auto urlToFile = adoptWK(Util::createURLForResource("simple", "html"));
+ auto pathToFile = adoptWK(WKURLCopyPath(urlToFile.get()));
+
+ WKContextPostMessageToInjectedBundle(context.get(), Util::toWK("TestFile").get(), pathToFile.get());
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKBundleFileHandle_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKBundleFileHandle_Bundle.cpp
new file mode 100644
index 000000000..27d359ba6
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKBundleFileHandle_Bundle.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundleFileHandleRef.h>
+#include <WebKit/WKBundleFrame.h>
+#include <WebKit/WKBundleScriptWorld.h>
+
+namespace TestWebKitAPI {
+
+static WKBundlePageRef loadedPage;
+
+class WKBundleFileHandleTest : public InjectedBundleTest {
+public:
+ WKBundleFileHandleTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+private:
+ virtual void didReceiveMessage(WKBundleRef bundle, WKStringRef messageName, WKTypeRef messageBody) override
+ {
+ if (!WKStringIsEqualToUTF8CString(messageName, "TestFile")) {
+ WKBundlePostMessage(bundle, Util::toWK("FAIL").get(), Util::toWK("Recieved invalid message").get());
+ return;
+ }
+
+ if (!loadedPage) {
+ WKBundlePostMessage(bundle, Util::toWK("FAIL").get(), Util::toWK("No loaded page").get());
+ return;
+ }
+
+ if (WKGetTypeID(messageBody) != WKStringGetTypeID()) {
+ WKBundlePostMessage(bundle, Util::toWK("FAIL").get(), Util::toWK("Message body has invalid type").get());
+ return;
+ }
+
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(loadedPage);
+ WKBundleScriptWorldRef world = WKBundleScriptWorldNormalWorld();
+
+ JSGlobalContextRef globalContext = WKBundleFrameGetJavaScriptContextForWorld(mainFrame, world);
+
+ auto fileHandle = adoptWK(WKBundleFileHandleCreateWithPath((WKStringRef)messageBody));
+ JSValueRef jsFileHandle = WKBundleFrameGetJavaScriptWrapperForFileForWorld(mainFrame, fileHandle.get(), world);
+
+ JSObjectRef globalObject = JSContextGetGlobalObject(globalContext);
+
+ JSStringRef jsString = JSStringCreateWithUTF8CString("testFile");
+ JSValueRef function = JSObjectGetProperty(globalContext, globalObject, jsString, nullptr);
+ JSStringRelease(jsString);
+
+ JSValueRef result = JSObjectCallAsFunction(globalContext, (JSObjectRef)function, globalObject, 1, &jsFileHandle, nullptr);
+
+ if (JSValueToBoolean(globalContext, result))
+ WKBundlePostMessage(bundle, Util::toWK("SUCCESS").get(), nullptr);
+ else
+ WKBundlePostMessage(bundle, Util::toWK("FAIL").get(), Util::toWK("Script failed").get());
+ }
+
+ virtual void didCreatePage(WKBundleRef bundle, WKBundlePageRef page) override
+ {
+ loadedPage = page;
+ }
+};
+
+static InjectedBundleTest::Register<WKBundleFileHandleTest> registrar("WKBundleFileHandleTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKImageCreateCGImageCrash.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKImageCreateCGImageCrash.cpp
new file mode 100644
index 000000000..f0d19025f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKImageCreateCGImageCrash.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include <WebKit/WKImageCG.h>
+
+namespace TestWebKitAPI {
+
+TEST(WebKit2, WKImageCreateCGImageCrash)
+{
+ EXPECT_FALSE(WKImageCreateCGImage(nullptr));
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKPageConfiguration.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKPageConfiguration.cpp
new file mode 100644
index 000000000..b5b850911
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKPageConfiguration.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+
+namespace TestWebKitAPI {
+
+TEST(WebKit2, WKPageConfigurationEmpty)
+{
+ WKRetainPtr<WKPageConfigurationRef> configuration = adoptWK(WKPageConfigurationCreate());
+
+ ASSERT_NULL(WKPageConfigurationGetContext(configuration.get()));
+ ASSERT_NULL(WKPageConfigurationGetUserContentController(configuration.get()));
+ ASSERT_NULL(WKPageConfigurationGetPageGroup(configuration.get()));
+ ASSERT_NULL(WKPageConfigurationGetPreferences(configuration.get()));
+ ASSERT_NULL(WKPageConfigurationGetRelatedPage(configuration.get()));
+}
+
+static bool didFinishLoad;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+TEST(WebKit2, WKPageConfigurationBasic)
+{
+ WKRetainPtr<WKPageConfigurationRef> configuration = adoptWK(WKPageConfigurationCreate());
+ WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreate());
+ WKPageConfigurationSetContext(configuration.get(), context.get());
+
+ PlatformWebView webView(configuration.get());
+ setPageLoaderClient(webView.page());
+
+ WKRetainPtr<WKURLRef> url = adoptWK(Util::createURLForResource("simple", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&didFinishLoad);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKPageCopySessionStateWithFiltering.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKPageCopySessionStateWithFiltering.cpp
new file mode 100644
index 000000000..6ce49a13b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKPageCopySessionStateWithFiltering.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+
+namespace TestWebKitAPI {
+
+static bool didFinishLoad;
+static WKRetainPtr<WKSessionStateRef> sessionStateWithFirstItemRemoved;
+static WKRetainPtr<WKSessionStateRef> sessionStateWithAllItemsRemoved;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+static bool filterFirstItemCallback(WKPageRef page, WKStringRef valueType, WKTypeRef value, void* context)
+{
+ if (!WKStringIsEqual(valueType, WKPageGetSessionBackForwardListItemValueType()))
+ return true;
+
+ ASSERT(WKGetTypeID(value) == WKBackForwardListItemGetTypeID());
+ WKBackForwardListItemRef backForwardListItem = static_cast<WKBackForwardListItemRef>(value);
+
+ WKRetainPtr<WKURLRef> url = adoptWK(WKBackForwardListItemCopyURL(backForwardListItem));
+ WKRetainPtr<WKStringRef> path = adoptWK(WKURLCopyLastPathComponent(url.get()));
+
+ return !WKStringIsEqualToUTF8CString(path.get(), "simple.html");
+}
+
+static bool filterAllItemsCallback(WKPageRef page, WKStringRef valueType, WKTypeRef value, void* context)
+{
+ return false;
+}
+
+static void createSessionStates(WKContextRef context)
+{
+ PlatformWebView webView(context);
+ setPageLoaderClient(webView.page());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple", "html")).get());
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple2", "html")).get());
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple3", "html")).get());
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ WKPageGoBack(webView.page());
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ WKPageGoBack(webView.page());
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ // Should be back on simple.html at this point.
+
+ sessionStateWithFirstItemRemoved = adoptWK(static_cast<WKSessionStateRef>(WKPageCopySessionState(webView.page(), reinterpret_cast<void*>(1), filterFirstItemCallback)));
+ sessionStateWithAllItemsRemoved = adoptWK(static_cast<WKSessionStateRef>(WKPageCopySessionState(webView.page(), reinterpret_cast<void*>(1), filterAllItemsCallback)));
+}
+
+TEST(WebKit2, WKPageCopySessionStateWithFiltering)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ createSessionStates(context.get());
+
+ EXPECT_NOT_NULL(sessionStateWithFirstItemRemoved);
+ PlatformWebView webView1(context.get());
+ setPageLoaderClient(webView1.page());
+ WKPageRestoreFromSessionState(webView1.page(), sessionStateWithFirstItemRemoved.get());
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ WKBackForwardListRef backForwardList1 = WKPageGetBackForwardList(webView1.page());
+ EXPECT_EQ(0u, WKBackForwardListGetBackListCount(backForwardList1));
+ EXPECT_EQ(1u, WKBackForwardListGetForwardListCount(backForwardList1));
+
+ EXPECT_NOT_NULL(sessionStateWithAllItemsRemoved);
+ PlatformWebView webView2(context.get());
+ setPageLoaderClient(webView2.page());
+ WKPageRestoreFromSessionState(webView2.page(), sessionStateWithAllItemsRemoved.get());
+ // Because the session state ends up being empty, nothing is actually loaded.
+
+ WKBackForwardListRef backForwardList2 = WKPageGetBackForwardList(webView2.page());
+ EXPECT_EQ(0u, WKBackForwardListGetBackListCount(backForwardList2));
+ EXPECT_EQ(0u, WKBackForwardListGetForwardListCount(backForwardList2));
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKPageGetScaleFactorNotZero.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKPageGetScaleFactorNotZero.cpp
new file mode 100644
index 000000000..929baaa71
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKPageGetScaleFactorNotZero.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+
+namespace TestWebKitAPI {
+
+static bool didFinishLoad;
+
+static void didFinishLoadForFrame(WKPageRef, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void setPageLoaderClient(WKPageRef page)
+{
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+}
+
+static WKRetainPtr<WKSessionStateRef> createSessionState(WKContextRef context)
+{
+ PlatformWebView webView(context);
+ setPageLoaderClient(webView.page());
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple", "html")).get());
+ Util::run(&didFinishLoad);
+ didFinishLoad = false;
+
+ return adoptWK(static_cast<WKSessionStateRef>(WKPageCopySessionState(webView.page(), reinterpret_cast<void*>(1), nullptr)));
+}
+
+TEST(WebKit2, WKPageGetScaleFactorNotZero)
+{
+ WKRetainPtr<WKContextRef> context(AdoptWK, WKContextCreate());
+
+ PlatformWebView webView(context.get());
+ setPageLoaderClient(webView.page());
+
+ auto sessionState = createSessionState(context.get());
+ EXPECT_NOT_NULL(sessionState);
+
+ WKPageRestoreFromSessionState(webView.page(), sessionState.get());
+ Util::run(&didFinishLoad);
+
+ EXPECT_TRUE(WKPageGetScaleFactor(webView.page()) == 1.0);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKPageIsPlayingAudio.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKPageIsPlayingAudio.cpp
new file mode 100644
index 000000000..44e7794f5
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKPageIsPlayingAudio.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <JavaScriptCore/JavaScriptCore.h>
+#include <WebKit/WKSerializedScriptValue.h>
+#include <WebKit/WKPagePrivate.h>
+#include <WebKit/WKPreferencesRef.h>
+#include <WebKit/WKPreferencesRefPrivate.h>
+
+// This test loads file-with-video.html. It first checks to make sure WKPageIsPlayingAudio() returns
+// false for the page. Then it calls a JavaScript method to play the video, waits for
+// WKPageUIClient::isPlayingAudioDidChange() to get called, and checks that WKPageIsPlayingAudio() now
+// returns true for the page.
+
+namespace TestWebKitAPI {
+
+static bool isMSEEnabledChanged;
+static bool isMSEEnabled;
+static bool didFinishLoad;
+static bool isPlayingAudioChanged;
+
+static void nullJavaScriptCallback(WKSerializedScriptValueRef, WKErrorRef error, void*)
+{
+}
+
+static void isMSEEnabledCallback(WKSerializedScriptValueRef serializedResultValue, WKErrorRef error, void*)
+{
+ JSGlobalContextRef scriptContext = JSGlobalContextCreate(0);
+
+ JSValueRef resultValue = WKSerializedScriptValueDeserialize(serializedResultValue, scriptContext, 0);
+ EXPECT_TRUE(JSValueIsBoolean(scriptContext, resultValue));
+
+ isMSEEnabledChanged = true;
+ isMSEEnabled = JSValueToBoolean(scriptContext, resultValue);
+
+ JSGlobalContextRelease(scriptContext);
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+static void isPlayingAudioDidChangeCallback(WKPageRef page, const void*)
+{
+ isPlayingAudioChanged = true;
+}
+
+static void setUpClients(WKPageRef page)
+{
+ WKPageLoaderClientV0 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 0;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(page, &loaderClient.base);
+
+ WKPageUIClientV4 uiClient;
+ memset(&uiClient, 0, sizeof(uiClient));
+
+ uiClient.base.version = 4;
+ uiClient.isPlayingAudioDidChange = isPlayingAudioDidChangeCallback;
+
+ WKPageSetPageUIClient(page, &uiClient.base);
+}
+
+TEST(WebKit2, WKPageIsPlayingAudio)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreate());
+
+ PlatformWebView webView(context.get());
+ setUpClients(webView.page());
+
+ WKRetainPtr<WKURLRef> url = adoptWK(Util::createURLForResource("file-with-video", "html"));
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&didFinishLoad);
+
+ EXPECT_FALSE(WKPageIsPlayingAudio(webView.page()));
+ WKPageRunJavaScriptInMainFrame(webView.page(), Util::toWK("playVideo()").get(), 0, nullJavaScriptCallback);
+
+ Util::run(&isPlayingAudioChanged);
+ EXPECT_TRUE(WKPageIsPlayingAudio(webView.page()));
+}
+
+TEST(WebKit2, MSEIsPlayingAudio)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreate());
+
+ WKRetainPtr<WKPageGroupRef> pageGroup(AdoptWK, WKPageGroupCreateWithIdentifier(Util::toWK("MSEIsPlayingAudioPageGroup").get()));
+ WKPreferencesRef preferences = WKPageGroupGetPreferences(pageGroup.get());
+ WKPreferencesSetMediaSourceEnabled(preferences, true);
+ WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
+
+ PlatformWebView webView(context.get(), pageGroup.get());
+ setUpClients(webView.page());
+
+ WKRetainPtr<WKURLRef> url = adoptWK(Util::createURLForResource("file-with-mse", "html"));
+ didFinishLoad = false;
+ WKPageLoadURL(webView.page(), url.get());
+
+ Util::run(&didFinishLoad);
+
+ // Bail out of the test early if the platform does not support MSE.
+ isMSEEnabledChanged = false;
+ WKPageRunJavaScriptInMainFrame(webView.page(), Util::toWK("window.MediaSource !== undefined").get(), 0, isMSEEnabledCallback);
+ Util::run(&isMSEEnabledChanged);
+ if (!isMSEEnabled)
+ return;
+
+ EXPECT_FALSE(WKPageIsPlayingAudio(webView.page()));
+ isPlayingAudioChanged = false;
+ WKPageRunJavaScriptInMainFrame(webView.page(), Util::toWK("playVideo()").get(), 0, nullJavaScriptCallback);
+
+ Util::run(&isPlayingAudioChanged);
+ EXPECT_TRUE(WKPageIsPlayingAudio(webView.page()));
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKPreferences.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKPreferences.cpp
new file mode 100644
index 000000000..7971b0b27
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKPreferences.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKPreferencesRefPrivate.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+TEST(WebKit2, WKPreferencesBasic)
+{
+ WKPreferencesRef preference = WKPreferencesCreate();
+
+ EXPECT_EQ(WKPreferencesGetTypeID(), WKGetTypeID(preference));
+
+ WKRelease(preference);
+}
+
+TEST(WebKit2, WKPreferencesDefaults)
+{
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ static const char* expectedStandardFontFamily = "Times";
+ static const char* expectedFixedFontFamily = "Courier New";
+ static const char* expectedSerifFontFamily = "Times";
+ static const char* expectedSansSerifFontFamily = "Helvetica";
+ static const char* expectedCursiveFontFamily = "Comic Sans MS";
+ static const char* expectedFantasyFontFamily = "Impact";
+ static const char* expectedPictographFontFamily = "Times";
+#elif WK_HAVE_C_SPI
+ static const char* expectedStandardFontFamily = "Times";
+ static const char* expectedFixedFontFamily = "Courier";
+ static const char* expectedSerifFontFamily = "Times";
+ static const char* expectedSansSerifFontFamily = "Helvetica";
+ static const char* expectedCursiveFontFamily = "Apple Chancery";
+ static const char* expectedFantasyFontFamily = "Papyrus";
+ static const char* expectedPictographFontFamily = "Apple Color Emoji";
+#elif PLATFORM(IOS)
+ static const char* expectedStandardFontFamily = "Times";
+ static const char* expectedFixedFontFamily = "Courier";
+ static const char* expectedSerifFontFamily = "Times";
+ static const char* expectedSansSerifFontFamily = "Helvetica";
+ static const char* expectedCursiveFontFamily = "Snell Roundhand";
+ static const char* expectedFantasyFontFamily = "Papyrus";
+ static const char* expectedPictographFontFamily = "AppleColorEmoji";
+#endif
+
+ WKPreferencesRef preference = WKPreferencesCreate();
+
+ EXPECT_TRUE(WKPreferencesGetJavaScriptEnabled(preference));
+ EXPECT_TRUE(WKPreferencesGetLoadsImagesAutomatically(preference));
+ EXPECT_FALSE(WKPreferencesGetOfflineWebApplicationCacheEnabled(preference));
+ EXPECT_TRUE(WKPreferencesGetLocalStorageEnabled(preference));
+ EXPECT_TRUE(WKPreferencesGetXSSAuditorEnabled(preference));
+ EXPECT_FALSE(WKPreferencesGetFrameFlatteningEnabled(preference));
+ EXPECT_TRUE(WKPreferencesGetPluginsEnabled(preference));
+ EXPECT_TRUE(WKPreferencesGetJavaEnabled(preference));
+ EXPECT_TRUE(WKPreferencesGetJavaScriptCanOpenWindowsAutomatically(preference));
+ EXPECT_TRUE(WKPreferencesGetHyperlinkAuditingEnabled(preference));
+ EXPECT_WK_STREQ(expectedStandardFontFamily, adoptWK(WKPreferencesCopyStandardFontFamily(preference)));
+ EXPECT_WK_STREQ(expectedFixedFontFamily, adoptWK(WKPreferencesCopyFixedFontFamily(preference)));
+ EXPECT_WK_STREQ(expectedSerifFontFamily, adoptWK(WKPreferencesCopySerifFontFamily(preference)));
+ EXPECT_WK_STREQ(expectedSansSerifFontFamily, adoptWK(WKPreferencesCopySansSerifFontFamily(preference)));
+ EXPECT_WK_STREQ(expectedCursiveFontFamily, adoptWK(WKPreferencesCopyCursiveFontFamily(preference)));
+ EXPECT_WK_STREQ(expectedFantasyFontFamily, adoptWK(WKPreferencesCopyFantasyFontFamily(preference)));
+ EXPECT_WK_STREQ(expectedPictographFontFamily, adoptWK(WKPreferencesCopyPictographFontFamily(preference)));
+ EXPECT_EQ(0u, WKPreferencesGetMinimumFontSize(preference));
+ EXPECT_FALSE(WKPreferencesGetPrivateBrowsingEnabled(preference));
+ EXPECT_FALSE(WKPreferencesGetDeveloperExtrasEnabled(preference));
+ EXPECT_TRUE(WKPreferencesGetTextAreasAreResizable(preference));
+
+ EXPECT_EQ(kWKFontSmoothingLevelMedium, WKPreferencesGetFontSmoothingLevel(preference));
+
+ EXPECT_TRUE(WKPreferencesGetAcceleratedCompositingEnabled(preference));
+ EXPECT_FALSE(WKPreferencesGetCompositingBordersVisible(preference));
+ EXPECT_FALSE(WKPreferencesGetCompositingRepaintCountersVisible(preference));
+ EXPECT_FALSE(WKPreferencesGetNeedsSiteSpecificQuirks(preference));
+ EXPECT_EQ(kWKAllowAllStorage, WKPreferencesGetStorageBlockingPolicy(preference));
+ EXPECT_FALSE(WKPreferencesGetTextAutosizingEnabled(preference));
+
+ WKRelease(preference);
+}
+
+TEST(WebKit2, WKPreferencesCopying)
+{
+ WKRetainPtr<WKStringRef> identifier(AdoptWK, WKStringCreateWithUTF8CString("identifier"));
+
+ WKRetainPtr<WKPreferencesRef> preferences(AdoptWK, WKPreferencesCreateWithIdentifier(identifier.get()));
+ WKPreferencesSetDefaultFontSize(preferences.get(), 36);
+
+ WKRetainPtr<WKPreferencesRef> copy(AdoptWK, WKPreferencesCreateCopy(preferences.get()));
+
+ WKPreferencesSetDefaultFontSize(preferences.get(), 24);
+ EXPECT_EQ(24u, WKPreferencesGetDefaultFontSize(preferences.get()));
+ EXPECT_EQ(36u, WKPreferencesGetDefaultFontSize(copy.get()));
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKString.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKString.cpp
new file mode 100644
index 000000000..b4167be81
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKString.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+namespace TestWebKitAPI {
+
+TEST(WebKit2, WKString)
+{
+ WKStringRef string = WKStringCreateWithUTF8CString("hello");
+ EXPECT_TRUE(!WKStringIsEmpty(string));
+ EXPECT_TRUE(WKStringIsEqual(string, string));
+ EXPECT_TRUE(WKStringIsEqualToUTF8CString(string, "hello"));
+ EXPECT_EQ(16u, WKStringGetMaximumUTF8CStringSize(string));
+
+ size_t maxSize = WKStringGetMaximumUTF8CStringSize(string);
+ char* buffer = new char[maxSize];
+
+ size_t actualSize = WKStringGetUTF8CString(string, buffer, maxSize);
+ EXPECT_EQ(6u, actualSize);
+ EXPECT_STREQ("hello", buffer);
+
+ delete[] buffer;
+
+ maxSize = WKStringGetLength(string);
+ EXPECT_EQ(5u, maxSize);
+
+ // Allocate a buffer one character larger than we need.
+ WKChar* uniBuffer = new WKChar[maxSize+1];
+ actualSize = WKStringGetCharacters(string, uniBuffer, maxSize);
+ EXPECT_EQ(5u, actualSize);
+
+ WKChar helloBuffer[] = { 'h', 'e', 'l', 'l', 'o' };
+ EXPECT_TRUE(!memcmp(uniBuffer, helloBuffer, 10));
+
+ // Test passing a buffer length < the string length.
+ actualSize = WKStringGetCharacters(string, uniBuffer, maxSize - 1);
+ EXPECT_EQ(4u, actualSize);
+
+ // Test passing a buffer length > the string length.
+ actualSize = WKStringGetCharacters(string, uniBuffer, maxSize + 1);
+ EXPECT_EQ(5u, actualSize);
+
+ delete[] uniBuffer;
+
+ WKRelease(string);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKStringJSString.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKStringJSString.cpp
new file mode 100644
index 000000000..8b6e08b9d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKStringJSString.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include <WebKit/WKStringPrivate.h>
+#include <JavaScriptCore/JSStringRef.h>
+
+namespace TestWebKitAPI {
+
+TEST(WebKit2, WKStringJSString)
+{
+ WKStringRef wkString = WKStringCreateWithUTF8CString("hello");
+ JSStringRef jsString = JSStringCreateWithUTF8CString("hello");
+
+ WKStringRef convertedJSString = WKStringCreateWithJSString(jsString);
+ EXPECT_TRUE(WKStringIsEqual(wkString, convertedJSString));
+
+ JSStringRef convertedWKString = WKStringCopyJSString(wkString);
+ EXPECT_TRUE(JSStringIsEqual(jsString, convertedWKString));
+
+ WKRelease(wkString);
+ WKRelease(convertedJSString);
+
+ JSStringRelease(jsString);
+ JSStringRelease(convertedWKString);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WKURL.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WKURL.cpp
new file mode 100644
index 000000000..f3ea83b6d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WKURL.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+namespace TestWebKitAPI {
+
+TEST(WebKit2, WKURL)
+{
+ WKURLRef baseURL = WKURLCreateWithUTF8CString("http://trac.webkit.org");
+ WKURLRef URL = WKURLCreateWithBaseURL(baseURL, "wiki");
+ WKRelease(baseURL);
+
+ WKStringRef string = WKURLCopyString(URL);
+ EXPECT_TRUE(WKStringIsEqualToUTF8CString(string, "http://trac.webkit.org/wiki"));
+
+ WKRelease(string);
+ WKRelease(URL);
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WebArchive.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WebArchive.cpp
new file mode 100644
index 000000000..06d673e21
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WebArchive.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <WebKit/WKURLCF.h>
+#include <WebKit/WKContextPrivate.h>
+#include <wtf/RetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static bool didFinishLoad;
+static bool didReceiveMessage;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef body, const void*)
+{
+ didReceiveMessage = true;
+
+ EXPECT_WK_STREQ("DidGetWebArchive", messageName);
+ EXPECT_TRUE(body);
+ EXPECT_EQ(WKDataGetTypeID(), WKGetTypeID(body));
+ WKDataRef receivedData = static_cast<WKDataRef>(body);
+
+ // Do basic sanity checks on the returned webarchive. We have more thorough checks in LayoutTests.
+ size_t size = WKDataGetSize(receivedData);
+ const unsigned char* bytes = WKDataGetBytes(receivedData);
+ RetainPtr<CFDataRef> data = adoptCF(CFDataCreate(0, bytes, size));
+ RetainPtr<CFPropertyListRef> propertyList = adoptCF(CFPropertyListCreateWithData(0, data.get(), kCFPropertyListImmutable, 0, 0));
+ EXPECT_TRUE(propertyList);
+
+ // It should be a dictionary.
+ EXPECT_EQ(CFDictionaryGetTypeID(), CFGetTypeID(propertyList.get()));
+ CFDictionaryRef dictionary = (CFDictionaryRef)propertyList.get();
+
+ // It should have a main resource.
+ CFTypeRef mainResource = CFDictionaryGetValue(dictionary, CFSTR("WebMainResource"));
+ EXPECT_TRUE(mainResource);
+ EXPECT_EQ(CFDictionaryGetTypeID(), CFGetTypeID(mainResource));
+ CFDictionaryRef mainResourceDictionary = (CFDictionaryRef)mainResource;
+
+ // Main resource should have a non-empty url and mime type.
+ CFTypeRef url = CFDictionaryGetValue(mainResourceDictionary, CFSTR("WebResourceURL"));
+ EXPECT_TRUE(url);
+ EXPECT_EQ(CFStringGetTypeID(), CFGetTypeID(url));
+ EXPECT_NE(CFStringGetLength((CFStringRef)url), 0);
+
+ CFTypeRef mimeType = CFDictionaryGetValue(mainResourceDictionary, CFSTR("WebResourceMIMEType"));
+ EXPECT_TRUE(mimeType);
+ EXPECT_EQ(CFStringGetTypeID(), CFGetTypeID(mimeType));
+ EXPECT_NE(CFStringGetLength((CFStringRef)mimeType), 0);
+
+ // Main resource dictionary should have a "WebResourceData" key.
+ CFTypeRef resourceData = CFDictionaryGetValue(mainResourceDictionary, CFSTR("WebResourceData"));
+ EXPECT_TRUE(resourceData);
+ EXPECT_EQ(CFDataGetTypeID(), CFGetTypeID(resourceData));
+
+ RetainPtr<CFStringRef> stringData = adoptCF(CFStringCreateFromExternalRepresentation(0, (CFDataRef)resourceData, kCFStringEncodingUTF8));
+ EXPECT_TRUE(stringData);
+
+ // It should contain the string "Simple HTML file." in it.
+ bool foundString = CFStringFind(stringData.get(), CFSTR("Simple HTML file."), 0).location != kCFNotFound;
+ EXPECT_TRUE(foundString);
+}
+
+static void setInjectedBundleClient(WKContextRef context)
+{
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+}
+
+static void didFinishLoadForFrame(WKPageRef page, WKFrameRef, WKTypeRef, const void*)
+{
+ didFinishLoad = true;
+}
+
+TEST(WebKit2, WebArchive)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("WebArchiveTest"));
+ setInjectedBundleClient(context.get());
+
+ PlatformWebView webView(context.get());
+
+ WKPageLoaderClientV3 loaderClient;
+ memset(&loaderClient, 0, sizeof(loaderClient));
+
+ loaderClient.base.version = 3;
+ loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
+
+ WKPageSetPageLoaderClient(webView.page(), &loaderClient.base);
+
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("simple", "html")).get());
+
+ // Wait till the load finishes before getting the web archive.
+ Util::run(&didFinishLoad);
+ WKContextPostMessageToInjectedBundle(context.get(), Util::toWK("GetWebArchive").get(), webView.page());
+
+ // Wait till we have received the web archive from the injected bundle.
+ Util::run(&didReceiveMessage);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WebArchive_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WebArchive_Bundle.cpp
new file mode 100644
index 000000000..11109fee0
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WebArchive_Bundle.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundleFrame.h>
+
+namespace TestWebKitAPI {
+
+class WebArchiveTest : public InjectedBundleTest {
+public:
+ WebArchiveTest(const std::string& identifier);
+
+private:
+ virtual void didReceiveMessage(WKBundleRef, WKStringRef messageName, WKTypeRef messageBody);
+};
+
+static InjectedBundleTest::Register<WebArchiveTest> registrar("WebArchiveTest");
+
+WebArchiveTest::WebArchiveTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+{
+}
+
+void WebArchiveTest::didReceiveMessage(WKBundleRef bundle, WKStringRef messageName, WKTypeRef body)
+{
+ if (!WKStringIsEqualToUTF8CString(messageName, "GetWebArchive"))
+ return;
+
+ if (WKGetTypeID(body) != WKBundlePageGetTypeID())
+ return;
+
+ WKBundleFrameRef frame = WKBundlePageGetMainFrame(static_cast<WKBundlePageRef>(body));
+ if (!frame)
+ return;
+ WKBundlePostMessage(bundle, Util::toWK("DidGetWebArchive").get(), adoptWK(WKBundleFrameCopyWebArchive(frame)).get());
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WebCoreStatisticsWithNoWebProcess.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WebCoreStatisticsWithNoWebProcess.cpp
new file mode 100644
index 000000000..ff5eef04d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WebCoreStatisticsWithNoWebProcess.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "Test.h"
+
+namespace TestWebKitAPI {
+
+static bool done;
+
+// Callback for WKContextGetStatistics.
+static void wkContextGetStatisticsCallback(WKDictionaryRef statistics, WKErrorRef error, void* functionContext)
+{
+ EXPECT_NOT_NULL(error);
+ done = true;
+}
+
+TEST(WebKit2, WebCoreStatisticsWithNoWebProcess)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreate());
+
+ WKContextGetStatistics(context.get(), 0, wkContextGetStatisticsCallback);
+
+ Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WillLoad.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WillLoad.cpp
new file mode 100644
index 000000000..83d63ab18
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WillLoad.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "Test.h"
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+
+namespace TestWebKitAPI {
+
+class WebKit2WillLoadTest : public ::testing::Test {
+public:
+ WebKit2WillLoadTest()
+ : didReceiveMessage(false)
+ {
+ }
+
+ WKRetainPtr<WKContextRef> context;
+ std::unique_ptr<PlatformWebView> webView;
+
+ WKRetainPtr<WKStringRef> messageName;
+ WKRetainPtr<WKTypeRef> messageBody;
+ bool didReceiveMessage;
+
+ static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+ {
+ ((WebKit2WillLoadTest*)clientInfo)->messageName = messageName;
+ ((WebKit2WillLoadTest*)clientInfo)->messageBody = messageBody;
+ ((WebKit2WillLoadTest*)clientInfo)->didReceiveMessage = true;
+ }
+
+ static void setInjectedBundleClient(WKContextRef context, const void* clientInfo)
+ {
+ WKContextInjectedBundleClientV1 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 1;
+ injectedBundleClient.base.clientInfo = clientInfo;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+ }
+
+ virtual void SetUp()
+ {
+ context = adoptWK(Util::createContextForInjectedBundleTest("WillLoadTest"));
+ setInjectedBundleClient(context.get(), this);
+
+ webView = std::make_unique<PlatformWebView>(context.get());
+
+ didReceiveMessage = false;
+ }
+
+ void testWillLoadURLRequestReturnValues(WKURLRef expectedURL, WKStringRef expectedUserDataString)
+ {
+ didReceiveMessage = false;
+ Util::run(&didReceiveMessage);
+
+ EXPECT_WK_STREQ("WillLoadURLRequestReturn", messageName.get());
+
+ EXPECT_EQ(WKDictionaryGetTypeID(), WKGetTypeID(messageBody.get()));
+ WKDictionaryRef dictionary = static_cast<WKDictionaryRef>(messageBody.get());
+
+ if (expectedUserDataString) {
+ WKStringRef userDataReturnValue = static_cast<WKStringRef>(WKDictionaryGetItemForKey(dictionary, Util::toWK("UserDataReturn").get()));
+ EXPECT_WK_STREQ(expectedUserDataString, userDataReturnValue);
+ } else
+ EXPECT_NULL(WKDictionaryGetItemForKey(dictionary, Util::toWK("UserDataReturn").get()));
+
+ WKURLRequestRef urlRequestReturnValue = static_cast<WKURLRequestRef>(WKDictionaryGetItemForKey(dictionary, Util::toWK("URLRequestReturn").get()));
+ WKRetainPtr<WKURLRef> urlReturnValue = adoptWK(WKURLRequestCopyURL(urlRequestReturnValue));
+ EXPECT_TRUE(WKURLIsEqual(expectedURL, urlReturnValue.get()));
+ }
+
+ void testWillLoadDataRequestReturnValues(WKURLRef expectedURL, WKStringRef expectedMIMEType, WKStringRef expectedEncodingName, WKURLRef expectedUnreachableURL, WKStringRef expectedUserDataString)
+ {
+ didReceiveMessage = false;
+ Util::run(&didReceiveMessage);
+
+ EXPECT_WK_STREQ("WillLoadDataRequestReturn", messageName.get());
+
+ EXPECT_EQ(WKDictionaryGetTypeID(), WKGetTypeID(messageBody.get()));
+ WKDictionaryRef dictionary = static_cast<WKDictionaryRef>(messageBody.get());
+
+ if (expectedUserDataString) {
+ WKStringRef userDataReturnValue = static_cast<WKStringRef>(WKDictionaryGetItemForKey(dictionary, Util::toWK("UserDataReturn").get()));
+ EXPECT_WK_STREQ(expectedUserDataString, userDataReturnValue);
+ } else
+ EXPECT_NULL(WKDictionaryGetItemForKey(dictionary, Util::toWK("UserDataReturn").get()));
+
+ WKURLRequestRef urlRequestReturnValue = static_cast<WKURLRequestRef>(WKDictionaryGetItemForKey(dictionary, Util::toWK("URLRequestReturn").get()));
+ WKRetainPtr<WKURLRef> urlReturnValue = adoptWK(WKURLRequestCopyURL(urlRequestReturnValue));
+ EXPECT_TRUE(WKURLIsEqual(expectedURL, urlReturnValue.get()));
+
+ WKStringRef MIMEType = static_cast<WKStringRef>(WKDictionaryGetItemForKey(dictionary, Util::toWK("MIMETypeReturn").get()));
+ EXPECT_WK_STREQ(expectedMIMEType, MIMEType);
+
+ WKStringRef encodingName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(dictionary, Util::toWK("EncodingNameReturn").get()));
+ EXPECT_WK_STREQ(expectedEncodingName, encodingName);
+
+ if (expectedUnreachableURL) {
+ WKURLRef unreachableURL = static_cast<WKURLRef>(WKDictionaryGetItemForKey(dictionary, Util::toWK("UnreachableURLReturn").get()));
+ EXPECT_TRUE(WKURLIsEqual(expectedUnreachableURL, unreachableURL));
+ } else
+ EXPECT_NULL(WKDictionaryGetItemForKey(dictionary, Util::toWK("UnreachableURLReturn").get()));
+ }
+};
+
+// URL Request tests
+
+TEST_F(WebKit2WillLoadTest, WKPageLoadURLWithUserData)
+{
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ WKRetainPtr<WKStringRef> userData = Util::toWK("WKPageLoadURLWithUserData UserData");
+ WKPageLoadURLWithUserData(webView->page(), url.get(), userData.get());
+
+ testWillLoadURLRequestReturnValues(url.get(), userData.get());
+}
+
+TEST_F(WebKit2WillLoadTest, WKPageLoadURL)
+{
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ WKPageLoadURL(webView->page(), url.get());
+
+ testWillLoadURLRequestReturnValues(url.get(), 0);
+}
+
+TEST_F(WebKit2WillLoadTest, WKPageLoadURLRequestWithUserData)
+{
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ WKRetainPtr<WKURLRequestRef> urlRequest = adoptWK(WKURLRequestCreateWithWKURL(url.get()));
+ WKRetainPtr<WKStringRef> userData = Util::toWK("WKPageLoadURLRequestWithUserData UserData");
+ WKPageLoadURLRequestWithUserData(webView->page(), urlRequest.get(), userData.get());
+
+ testWillLoadURLRequestReturnValues(url.get(), userData.get());
+}
+
+TEST_F(WebKit2WillLoadTest, WKPageLoadURLRequest)
+{
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ WKRetainPtr<WKURLRequestRef> urlRequest = adoptWK(WKURLRequestCreateWithWKURL(url.get()));
+ WKPageLoadURLRequest(webView->page(), urlRequest.get());
+
+ testWillLoadURLRequestReturnValues(url.get(), 0);
+}
+
+// Data Request tests
+
+TEST_F(WebKit2WillLoadTest, WKPageLoadHTMLStringWithUserData)
+{
+ WKRetainPtr<WKURLRef> baseURL = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ WKRetainPtr<WKStringRef> userData = Util::toWK("WKPageLoadHTMLStringWithUserData UserData");
+ WKRetainPtr<WKStringRef> htmlString = Util::toWK("<body>Hello, World</body>");
+
+ WKPageLoadHTMLStringWithUserData(webView->page(), htmlString.get(), baseURL.get(), userData.get());
+
+ testWillLoadDataRequestReturnValues(baseURL.get(), Util::toWK("text/html").get(), Util::toWK("latin1").get(), 0, userData.get());
+}
+
+TEST_F(WebKit2WillLoadTest, WKPageLoadHTMLString)
+{
+ WKRetainPtr<WKURLRef> baseURL = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ WKRetainPtr<WKStringRef> htmlString = Util::toWK("<body>Hello, World</body>");
+
+ WKPageLoadHTMLString(webView->page(), htmlString.get(), baseURL.get());
+
+ testWillLoadDataRequestReturnValues(baseURL.get(), Util::toWK("text/html").get(), Util::toWK("latin1").get(), 0, 0);
+}
+
+TEST_F(WebKit2WillLoadTest, WKPageLoadAlternateHTMLStringWithUserData)
+{
+ WKRetainPtr<WKStringRef> htmlString = Util::toWK("<body>Hello, World</body>");
+
+ WKRetainPtr<WKURLRef> baseURL = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ WKRetainPtr<WKURLRef> unreachableURL = adoptWK(WKURLCreateWithUTF8CString("about:other"));
+ WKRetainPtr<WKStringRef> userData = Util::toWK("WKPageLoadAlternateHTMLStringWithUserData UserData");
+
+ WKPageLoadAlternateHTMLStringWithUserData(webView->page(), htmlString.get(), baseURL.get(), unreachableURL.get(), userData.get());
+
+ testWillLoadDataRequestReturnValues(baseURL.get(), Util::toWK("text/html").get(), Util::toWK("latin1").get(), unreachableURL.get(), userData.get());
+}
+
+TEST_F(WebKit2WillLoadTest, WKPageLoadAlternateHTMLString)
+{
+ WKRetainPtr<WKStringRef> htmlString = Util::toWK("<body>Hello, World</body>");
+
+ WKRetainPtr<WKURLRef> baseURL = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ WKRetainPtr<WKURLRef> unreachableURL = adoptWK(WKURLCreateWithUTF8CString("about:other"));
+
+ WKPageLoadAlternateHTMLString(webView->page(), htmlString.get(), baseURL.get(), unreachableURL.get());
+
+ testWillLoadDataRequestReturnValues(baseURL.get(), Util::toWK("text/html").get(), Util::toWK("latin1").get(), unreachableURL.get(), 0);
+}
+
+TEST_F(WebKit2WillLoadTest, WKPageLoadPlainTextStringWithUserData)
+{
+ WKRetainPtr<WKStringRef> plaintTextString = Util::toWK("Hello, World");
+ WKRetainPtr<WKStringRef> userData = Util::toWK("WKPageLoadPlainTextStringWithUserData UserData");
+
+ WKPageLoadPlainTextStringWithUserData(webView->page(), plaintTextString.get(), userData.get());
+
+ WKRetainPtr<WKURLRef> blankURL = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ testWillLoadDataRequestReturnValues(blankURL.get(), Util::toWK("text/plain").get(), Util::toWK("latin1").get(), 0, userData.get());
+}
+
+TEST_F(WebKit2WillLoadTest, WKPageLoadPlainTextString)
+{
+ WKRetainPtr<WKStringRef> plaintTextString = Util::toWK("Hello, World");
+
+ WKPageLoadPlainTextString(webView->page(), plaintTextString.get());
+
+ WKRetainPtr<WKURLRef> blankURL = adoptWK(WKURLCreateWithUTF8CString("about:blank"));
+ testWillLoadDataRequestReturnValues(blankURL.get(), Util::toWK("text/plain").get(), Util::toWK("latin1").get(), 0, 0);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WillLoad_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WillLoad_Bundle.cpp
new file mode 100644
index 000000000..091e5a942
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WillLoad_Bundle.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKRetainPtr.h>
+
+namespace TestWebKitAPI {
+
+class WillLoadTest : public InjectedBundleTest {
+public:
+ WillLoadTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+private:
+ static void willLoadURLRequest(WKBundlePageRef page, WKURLRequestRef request, WKTypeRef userData, const void *clientInfo)
+ {
+ WKRetainPtr<WKMutableDictionaryRef> messageBody = adoptWK(WKMutableDictionaryCreate());
+
+ WKDictionarySetItem(messageBody.get(), Util::toWK("URLRequestReturn").get(), request);
+ WKDictionarySetItem(messageBody.get(), Util::toWK("UserDataReturn").get(), userData);
+
+ WKBundlePostMessage(InjectedBundleController::singleton().bundle(), Util::toWK("WillLoadURLRequestReturn").get(), messageBody.get());
+ }
+
+ static void willLoadDataRequest(WKBundlePageRef page, WKURLRequestRef request, WKDataRef data, WKStringRef MIMEType, WKStringRef encodingName, WKURLRef unreachableURL, WKTypeRef userData, const void *clientInfo)
+ {
+ WKRetainPtr<WKMutableDictionaryRef> messageBody = adoptWK(WKMutableDictionaryCreate());
+
+ WKDictionarySetItem(messageBody.get(), Util::toWK("URLRequestReturn").get(), request);
+ WKDictionarySetItem(messageBody.get(), Util::toWK("DataReturn").get(), data);
+ WKDictionarySetItem(messageBody.get(), Util::toWK("MIMETypeReturn").get(), MIMEType);
+ WKDictionarySetItem(messageBody.get(), Util::toWK("EncodingNameReturn").get(), encodingName);
+ WKDictionarySetItem(messageBody.get(), Util::toWK("UnreachableURLReturn").get(), unreachableURL);
+ WKDictionarySetItem(messageBody.get(), Util::toWK("UserDataReturn").get(), userData);
+
+ WKBundlePostMessage(InjectedBundleController::singleton().bundle(), Util::toWK("WillLoadDataRequestReturn").get(), messageBody.get());
+
+ }
+
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef bundlePage) override
+ {
+ WKBundlePageLoaderClientV6 pageLoaderClient;
+ memset(&pageLoaderClient, 0, sizeof(pageLoaderClient));
+
+ pageLoaderClient.base.version = 6;
+ pageLoaderClient.base.clientInfo = this;
+ pageLoaderClient.willLoadURLRequest = willLoadURLRequest;
+ pageLoaderClient.willLoadDataRequest = willLoadDataRequest;
+
+ WKBundlePageSetPageLoaderClient(bundlePage, &pageLoaderClient.base);
+ }
+};
+
+static InjectedBundleTest::Register<WillLoadTest> registrar("WillLoadTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WillSendSubmitEvent.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WillSendSubmitEvent.cpp
new file mode 100644
index 000000000..951aaf005
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WillSendSubmitEvent.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "PlatformUtilities.h"
+#include "PlatformWebView.h"
+#include "Test.h"
+
+namespace TestWebKitAPI {
+
+static bool didReceiveMessage;
+
+static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef body, const void*)
+{
+ didReceiveMessage = true;
+
+ EXPECT_WK_STREQ("DidReceiveWillSendSubmitEvent", messageName);
+
+ EXPECT_EQ(WKDictionaryGetTypeID(), WKGetTypeID(body));
+ WKDictionaryRef values = static_cast<WKDictionaryRef>(body);
+
+ WKRetainPtr<WKStringRef> textFieldKey(AdoptWK, WKStringCreateWithUTF8CString("textField"));
+ WKStringRef textFieldValueWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(values, textFieldKey.get()));
+ EXPECT_WK_STREQ("text field", textFieldValueWK);
+
+ WKRetainPtr<WKStringRef> passwordFieldKey(AdoptWK, WKStringCreateWithUTF8CString("passwordField"));
+ WKStringRef passwordFieldValueWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(values, passwordFieldKey.get()));
+ EXPECT_WK_STREQ("password field", passwordFieldValueWK);
+
+ // <input type="hidden"> fields are not sent.
+ WKRetainPtr<WKStringRef> hiddenFieldKey(AdoptWK, WKStringCreateWithUTF8CString("hiddenField"));
+ WKStringRef hiddenFieldValueWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(values, hiddenFieldKey.get()));
+ EXPECT_NULL(hiddenFieldValueWK);
+}
+
+static void setInjectedBundleClient(WKContextRef context)
+{
+ WKContextInjectedBundleClientV0 injectedBundleClient;
+ memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));
+
+ injectedBundleClient.base.version = 0;
+ injectedBundleClient.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle;
+
+ WKContextSetInjectedBundleClient(context, &injectedBundleClient.base);
+}
+
+TEST(WebKit2, WillSendSubmitEvent)
+{
+ WKRetainPtr<WKContextRef> context = adoptWK(Util::createContextForInjectedBundleTest("WillSendSubmitEventTest"));
+ setInjectedBundleClient(context.get());
+
+ PlatformWebView webView(context.get());
+ WKPageLoadURL(webView.page(), adoptWK(Util::createURLForResource("auto-submitting-form", "html")).get());
+ Util::run(&didReceiveMessage);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/WillSendSubmitEvent_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/WillSendSubmitEvent_Bundle.cpp
new file mode 100644
index 000000000..fa7d2a9a7
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/WillSendSubmitEvent_Bundle.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+
+#include "PlatformUtilities.h"
+#include <WebKit/WKBundlePage.h>
+
+namespace TestWebKitAPI {
+
+class WillSendSubmitEventTest : public InjectedBundleTest {
+public:
+ WillSendSubmitEventTest(const std::string& identifier);
+
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef);
+};
+
+static InjectedBundleTest::Register<WillSendSubmitEventTest> registrar("WillSendSubmitEventTest");
+
+static void willSendSubmitEvent(WKBundlePageRef, WKBundleNodeHandleRef, WKBundleFrameRef, WKBundleFrameRef, WKDictionaryRef values, const void*)
+{
+ WKBundlePostMessage(InjectedBundleController::singleton().bundle(), Util::toWK("DidReceiveWillSendSubmitEvent").get(), values);
+}
+
+WillSendSubmitEventTest::WillSendSubmitEventTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+{
+}
+
+void WillSendSubmitEventTest::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+{
+ WKBundlePageFormClientV1 formClient;
+ memset(&formClient, 0, sizeof(formClient));
+
+ formClient.base.version = 1;
+ formClient.base.clientInfo = this;
+ formClient.willSendSubmitEvent = willSendSubmitEvent;
+
+ WKBundlePageSetFormClient(page, &formClient.base);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/all-content-in-one-iframe.html b/Tools/TestWebKitAPI/Tests/WebKit2/all-content-in-one-iframe.html
new file mode 100644
index 000000000..6f45cbd6b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/all-content-in-one-iframe.html
@@ -0,0 +1 @@
+<iframe src="lots-of-text.html"></iframe>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/associate-form-controls.html b/Tools/TestWebKitAPI/Tests/WebKit2/associate-form-controls.html
new file mode 100644
index 000000000..906246a54
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/associate-form-controls.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+ <script>
+ function addForm()
+ {
+ var form = document.createElement("form");
+ form.id = "login_form";
+
+ var usernameField = document.createElement("input");
+ usernameField.id = "username";
+ usernameField.type = "text";
+ form.appendChild(usernameField);
+
+ document.body.appendChild(form);
+ }
+
+ function addPasswordFieldToForm()
+ {
+ var passwordField = document.createElement("input");
+ passwordField.id = "password";
+ passwordField.type = "password";
+
+ var form = document.getElementById("login_form");
+ form.appendChild(passwordField);
+ }
+ </script>
+</head>
+<body onload="addForm()">
+<button onclick="addPasswordFieldToForm()">Manual Testing: Add the password field</button>
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/auto-submitting-form.html b/Tools/TestWebKitAPI/Tests/WebKit2/auto-submitting-form.html
new file mode 100644
index 000000000..9ed815e03
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/auto-submitting-form.html
@@ -0,0 +1,20 @@
+<html>
+ <head>
+ <script>
+ function submitFormIfNecessary()
+ {
+ if (window.location.search)
+ return;
+ document.forms[0].submit.click();
+ }
+ </script>
+ </head>
+ <body onload="submitFormIfNecessary()">
+ <form action="#" method="GET">
+ <input type="text" name="textField" value="text field">
+ <input type="password" name="passwordField" value="password field">
+ <input type="hidden" name="hiddenField" value="hidden field">
+ <input type="submit" name="submit">
+ </form>
+ </body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/bundle-file.html b/Tools/TestWebKitAPI/Tests/WebKit2/bundle-file.html
new file mode 100644
index 000000000..f426ebdb0
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/bundle-file.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+ <script>
+ function testFile(file)
+ {
+ if (!(file instanceof File))
+ return false;
+
+ return true;
+ }
+ </script>
+</head>
+<body>
+ File test.
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/close-from-within-create-page.html b/Tools/TestWebKitAPI/Tests/WebKit2/close-from-within-create-page.html
new file mode 100644
index 000000000..51c7981c1
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/close-from-within-create-page.html
@@ -0,0 +1,16 @@
+<html>
+<script>
+function runTest()
+{
+ if (document.location.search === "?opened-window") {
+ alert(window.sessionStorage['storageKey'])
+ return;
+ }
+
+ window.sessionStorage['storageKey'] = 'value';
+ window.open("close-from-within-create-page.html?opened-window");
+}
+</script>
+<body onload="runTest()">
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/custom-protocol-sync-xhr.html b/Tools/TestWebKitAPI/Tests/WebKit2/custom-protocol-sync-xhr.html
new file mode 100644
index 000000000..8a98f33e9
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/custom-protocol-sync-xhr.html
@@ -0,0 +1,6 @@
+<script>
+ var request = new XMLHttpRequest();
+ request.open('GET', 'test://test', false);
+ request.send(null);
+ window._testResult = request.responseText;
+</script> \ No newline at end of file
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/execCopy.html b/Tools/TestWebKitAPI/Tests/WebKit2/execCopy.html
new file mode 100644
index 000000000..cf4608ebd
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/execCopy.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+<script type="text/javascript">
+ function doCopy() {
+ document.execCommand("selectAll", true);
+ document.execCommand("copy");
+ }
+</script>
+</head>
+<body onLoad="doCopy()">
+ <div>Hello world.</div>
+ <div><b>Hello</b><i> world</i></div>
+ <div style="border: solid red 1px">Hello world</div>
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/file-with-anchor.html b/Tools/TestWebKitAPI/Tests/WebKit2/file-with-anchor.html
new file mode 100644
index 000000000..8ea866ba9
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/file-with-anchor.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+ <script>
+ function clickLink()
+ {
+ var evt = document.createEvent("MouseEvent");
+ evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ var link = document.querySelector('a');
+ link.dispatchEvent(evt);
+ }
+ </script>
+</head>
+<body>
+ <a href="#anchor">Link to anchor</a><br>
+ In between.<br>
+ <span id="anchor">Anchor</span><br>
+ After the anchor.<br>
+ </body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/file-with-mse.html b/Tools/TestWebKitAPI/Tests/WebKit2/file-with-mse.html
new file mode 100644
index 000000000..413eed6e8
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/file-with-mse.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script>
+ var source;
+ var request;
+
+ function playVideo()
+ {
+ request = new XMLHttpRequest();
+ request.responseType = 'arraybuffer';
+ request.open('GET', 'test-mse.mp4', true);
+ request.addEventListener('load', load);
+ request.send();
+ }
+
+ function load(event)
+ {
+ source = new MediaSource();
+ source.addEventListener('sourceopen', sourceopen);
+ var video = document.getElementById('test-video');
+ video.src = URL.createObjectURL(source);
+ }
+
+ function sourceopen(event)
+ {
+ var sourceBuffer = source.addSourceBuffer('video/mp4;codecs="avc1.4D4001,mp4a.40.2"');
+ sourceBuffer.appendBuffer(request.response);
+ sourceBuffer.addEventListener('updateend', updateend);
+ }
+
+ function updateend(event)
+ {
+ document.getElementById('test-video').play();
+ }
+ </script>
+</head>
+<body>
+ <p>
+ <video id="test-video" controls></video>
+ </p>
+ <p>
+ <button onclick="playVideo()">Play video</button>
+ </p>
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/file-with-video.html b/Tools/TestWebKitAPI/Tests/WebKit2/file-with-video.html
new file mode 100644
index 000000000..aa2259a0f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/file-with-video.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+ <script>
+ function playVideo()
+ {
+ document.getElementById("test-video").play();
+ }
+ </script>
+</head>
+<body>
+ <p>
+ <video id="test-video" src="test.mp4" controls></video>
+ </p>
+ <p>
+ <button onclick="playVideo()">Play Video</button>
+ </p>
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/find.html b/Tools/TestWebKitAPI/Tests/WebKit2/find.html
new file mode 100644
index 000000000..d9659119d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/find.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ Test search. Hello Hello Hello!
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/findRanges.html b/Tools/TestWebKitAPI/Tests/WebKit2/findRanges.html
new file mode 100644
index 000000000..e10bbeb47
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/findRanges.html
@@ -0,0 +1,11 @@
+<html>
+<body>Test search. Hello world, Hello crazy world, Hello!</body>
+<script>
+ var s = window.getSelection();
+ var r = document.createRange();
+
+ r.setStart(document.body.firstChild, 38);
+ r.setEnd(document.body.firstChild, 43);
+ s.addRange(r);
+</script>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/geolocationGetCurrentPosition.html b/Tools/TestWebKitAPI/Tests/WebKit2/geolocationGetCurrentPosition.html
new file mode 100644
index 000000000..099f51536
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/geolocationGetCurrentPosition.html
@@ -0,0 +1,3 @@
+<script>
+navigator.geolocation.getCurrentPosition(function() { });
+</script>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/geolocationGetCurrentPositionWithHighAccuracy.html b/Tools/TestWebKitAPI/Tests/WebKit2/geolocationGetCurrentPositionWithHighAccuracy.html
new file mode 100644
index 000000000..26a314858
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/geolocationGetCurrentPositionWithHighAccuracy.html
@@ -0,0 +1,3 @@
+<script>
+navigator.geolocation.getCurrentPosition(function() { }, function() {}, { enableHighAccuracy:true });
+</script>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/geolocationWatchPosition.html b/Tools/TestWebKitAPI/Tests/WebKit2/geolocationWatchPosition.html
new file mode 100644
index 000000000..24788b780
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/geolocationWatchPosition.html
@@ -0,0 +1,3 @@
+<script>
+navigator.geolocation.watchPosition(function() { });
+</script>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/geolocationWatchPositionWithHighAccuracy.html b/Tools/TestWebKitAPI/Tests/WebKit2/geolocationWatchPositionWithHighAccuracy.html
new file mode 100644
index 000000000..aba98fce7
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/geolocationWatchPositionWithHighAccuracy.html
@@ -0,0 +1,3 @@
+<script>
+navigator.geolocation.watchPosition(function() { }, function() {}, { enableHighAccuracy:true });
+</script>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/getUserMedia.html b/Tools/TestWebKitAPI/Tests/WebKit2/getUserMedia.html
new file mode 100644
index 000000000..e54f853bf
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/getUserMedia.html
@@ -0,0 +1,14 @@
+<script>
+function gotUserMedia(mediaStream)
+{
+ console.log("Got user media");
+}
+
+function userMediaError(error)
+{
+ console.log(error);
+}
+
+var options = { audio: false, video: true};
+navigator.webkitGetUserMedia(options, gotUserMedia, userMediaError);
+</script>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/gtk/InputMethodFilter.cpp b/Tools/TestWebKitAPI/Tests/WebKit2/gtk/InputMethodFilter.cpp
new file mode 100644
index 000000000..4a0e684d6
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/gtk/InputMethodFilter.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "WTFStringUtilities.h"
+#include <WebKit/InputMethodFilter.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/CString.h>
+
+using namespace WebKit;
+
+namespace TestWebKitAPI {
+
+class TestInputMethodFilter : public InputMethodFilter {
+public:
+ TestInputMethodFilter()
+ : m_testWindow(gtk_window_new(GTK_WINDOW_POPUP))
+ {
+ setTestingMode(true);
+
+ gtk_widget_show(m_testWindow);
+ gtk_im_context_set_client_window(context(), gtk_widget_get_window(m_testWindow));
+
+ setEnabled(true);
+ }
+
+ ~TestInputMethodFilter()
+ {
+ gtk_widget_destroy(m_testWindow);
+ }
+
+ void sendKeyEventToFilter(unsigned gdkKeyValue, GdkEventType type, unsigned modifiers = 0)
+ {
+ GdkEvent* event = gdk_event_new(type);
+ event->key.keyval = gdkKeyValue;
+ event->key.state = modifiers;
+ event->key.window = gtk_widget_get_window(m_testWindow);
+ event->key.time = GDK_CURRENT_TIME;
+ g_object_ref(event->key.window);
+ gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())));
+
+ GUniqueOutPtr<GdkKeymapKey> keys;
+ gint nKeys;
+ if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeyValue, &keys.outPtr(), &nKeys))
+ event->key.hardware_keycode = keys.get()[0].keycode;
+
+ filterKeyEvent(&event->key);
+ gdk_event_free(event);
+ }
+
+ void sendPressAndReleaseKeyEventPairToFilter(unsigned gdkKeyValue, unsigned modifiers = 0)
+ {
+ sendKeyEventToFilter(gdkKeyValue, GDK_KEY_PRESS, modifiers);
+ sendKeyEventToFilter(gdkKeyValue, GDK_KEY_RELEASE, modifiers);
+ }
+
+private:
+ GtkWidget* m_testWindow;
+};
+
+TEST(WebKit2, InputMethodFilterSimple)
+{
+ TestInputMethodFilter inputMethodFilter;
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_g);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_t);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_k);
+
+ const Vector<String>& events = inputMethodFilter.events();
+
+ ASSERT_EQ(6, events.size());
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=67 text='g'"), events[0]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=67"), events[1]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=74 text='t'"), events[2]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=74"), events[3]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=6b text='k'"), events[4]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=6b"), events[5]);
+}
+
+TEST(WebKit2, InputMethodFilterUnicodeSequence)
+{
+ TestInputMethodFilter inputMethodFilter;
+
+ // This is simple unicode hex entry of the characters, u, 0, 0, f, 4 pressed with
+ // the shift and controls keys held down. In reality, these values are not typical
+ // of an actual hex entry, because they'd be transformed by the shift modifier according
+ // to the keyboard layout. For instance, on a US keyboard a 0 with the shift key pressed
+ // is a right parenthesis. Using these values prevents having to work out what the
+ // transformed characters are based on the current keyboard layout.
+ inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Control_L, GDK_KEY_PRESS);
+ inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Shift_L, GDK_KEY_PRESS, GDK_CONTROL_MASK);
+
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_U, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_0, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_0, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_F, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_4, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+
+ inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Shift_L, GDK_KEY_RELEASE, GDK_CONTROL_MASK | GDK_SHIFT_MASK);
+ inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Control_L, GDK_KEY_RELEASE, GDK_CONTROL_MASK);
+
+ const Vector<String>& events = inputMethodFilter.events();
+ ASSERT_EQ(21, events.size());
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=ffe3"), events[0]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=ffe1"), events[1]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=85"), events[2]);
+ ASSERT_EQ(String("setPreedit text='u' cursorOffset=1"), events[3]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=55"), events[4]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=48"), events[5]);
+ ASSERT_EQ(String("setPreedit text='u0' cursorOffset=2"), events[6]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=30"), events[7]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=48"), events[8]);
+ ASSERT_EQ(String("setPreedit text='u00' cursorOffset=3"), events[9]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=30"), events[10]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=70"), events[11]);
+ ASSERT_EQ(String("setPreedit text='u00F' cursorOffset=4"), events[12]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=46"), events[13]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=52"), events[14]);
+ ASSERT_EQ(String("setPreedit text='u00F4' cursorOffset=5"), events[15]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=34"), events[16]);
+ ASSERT_EQ(String("confirmComposition 'ô'"), events[17]);
+ ASSERT_EQ(String("setPreedit text='' cursorOffset=0"), events[18]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffe1"), events[19]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffe3"), events[20]);
+}
+
+TEST(WebKit2, InputMethodFilterComposeKey)
+{
+ TestInputMethodFilter inputMethodFilter;
+
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_Multi_key);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_apostrophe);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_o);
+
+ const Vector<String>& events = inputMethodFilter.events();
+ ASSERT_EQ(5, events.size());
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=39"), events[0]);
+ ASSERT_EQ(String("setPreedit text='' cursorOffset=0"), events[1]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=27"), events[2]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=6f text='ó'"), events[3]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=6f"), events[4]);
+}
+
+typedef void (*GetPreeditStringCallback) (GtkIMContext*, gchar**, PangoAttrList**, int*);
+static void temporaryGetPreeditStringOverride(GtkIMContext*, char** string, PangoAttrList** attrs, int* cursorPosition)
+{
+ *string = g_strdup("preedit of doom, bringer of cheese");
+ *cursorPosition = 3;
+}
+
+TEST(WebKit2, InputMethodFilterContextEventsWithoutKeyEvents)
+{
+ TestInputMethodFilter inputMethodFilter;
+
+ // This is a bit of a hack to avoid mocking out the entire InputMethodContext, by
+ // simply replacing the get_preedit_string virtual method for the length of this test.
+ GtkIMContext* context = inputMethodFilter.context();
+ GtkIMContextClass* contextClass = GTK_IM_CONTEXT_GET_CLASS(context);
+ GetPreeditStringCallback previousCallback = contextClass->get_preedit_string;
+ contextClass->get_preedit_string = temporaryGetPreeditStringOverride;
+
+ g_signal_emit_by_name(context, "preedit-changed");
+ g_signal_emit_by_name(context, "commit", "commit text");
+
+ contextClass->get_preedit_string = previousCallback;
+
+ const Vector<String>& events = inputMethodFilter.events();
+ ASSERT_EQ(6, events.size());
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=16777215 (faked)"), events[0]);
+ ASSERT_EQ(String("setPreedit text='preedit of doom, bringer of cheese' cursorOffset=3"), events[1]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffffff (faked)"), events[2]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=16777215 (faked)"), events[3]);
+ ASSERT_EQ(String("confirmComposition 'commit text'"), events[4]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffffff (faked)"), events[5]);
+}
+
+static bool gSawContextReset = false;
+typedef void (*ResetCallback) (GtkIMContext*);
+static void temporaryResetOverride(GtkIMContext*)
+{
+ gSawContextReset = true;
+}
+
+static void verifyCanceledComposition(const Vector<String>& events)
+{
+ ASSERT_EQ(3, events.size());
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=39"), events[0]);
+ ASSERT_EQ(String("setPreedit text='' cursorOffset=0"), events[1]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=27"), events[2]);
+ ASSERT(gSawContextReset);
+}
+
+TEST(WebKit2, InputMethodFilterContextFocusOutDuringOngoingComposition)
+{
+ TestInputMethodFilter inputMethodFilter;
+
+ // See comment above about this technique.
+ GtkIMContext* context = inputMethodFilter.context();
+ GtkIMContextClass* contextClass = GTK_IM_CONTEXT_GET_CLASS(context);
+ ResetCallback previousCallback = contextClass->reset;
+ contextClass->reset = temporaryResetOverride;
+
+ gSawContextReset = false;
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_Multi_key);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_apostrophe);
+ inputMethodFilter.notifyFocusedOut();
+
+ verifyCanceledComposition(inputMethodFilter.events());
+
+ contextClass->reset = previousCallback;
+}
+
+TEST(WebKit2, InputMethodFilterContextMouseClickDuringOngoingComposition)
+{
+ TestInputMethodFilter inputMethodFilter;
+
+ // See comment above about this technique.
+ GtkIMContext* context = inputMethodFilter.context();
+ GtkIMContextClass* contextClass = GTK_IM_CONTEXT_GET_CLASS(context);
+ ResetCallback previousCallback = contextClass->reset;
+ contextClass->reset = temporaryResetOverride;
+
+ gSawContextReset = false;
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_Multi_key);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_apostrophe);
+ inputMethodFilter.notifyMouseButtonPress();
+
+ verifyCanceledComposition(inputMethodFilter.events());
+
+ contextClass->reset = previousCallback;
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/icon.png b/Tools/TestWebKitAPI/Tests/WebKit2/icon.png
new file mode 100644
index 000000000..79e459894
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/icon.png
Binary files differ
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/input-focus-blur.html b/Tools/TestWebKitAPI/Tests/WebKit2/input-focus-blur.html
new file mode 100644
index 000000000..c06439e66
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/input-focus-blur.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<body>
+<input id="input" type="text">
+<input id="readonly" type="text" readonly>
+<script>
+function focusTextField(id)
+{
+ document.getElementById(id).focus();
+}
+
+function blurTextField(id)
+{
+ document.getElementById(id).blur();
+}
+</script>
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/link-with-title.html b/Tools/TestWebKitAPI/Tests/WebKit2/link-with-title.html
new file mode 100644
index 000000000..971ed281b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/link-with-title.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ <a href="#" style="display: block; height: 100%;" title="HitTestLinkTitle"></a>
+ </body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-iframes.html b/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-iframes.html
new file mode 100644
index 000000000..5436310c8
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-iframes.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+<script type="text/javascript">
+ var maxNumberOfFrames = 100;
+
+ function createIFrames() {
+ var str = "<div id=\"status\"></div>";
+ for (var i = 0; i < maxNumberOfFrames + 1; i++) {
+ str += "<iframe id=\"i" + i + "\" src=\"data:text/html,iframe_" + i + "\"></iframe>";
+ }
+ document.getElementsByTagName("body")[0].innerHTML = str;
+
+ var results = "";
+
+ var f = document.getElementById("i" + (maxNumberOfFrames - 1));
+ if (f && f.contentWindow) {
+ results += "Sucessfully created " + maxNumberOfFrames + " frames.<br>";
+ } else {
+ results += "Failed to create " + maxNumberOfFrames + " frames.<br>";
+ }
+
+ var g = document.getElementById("i" + maxNumberOfFrames);
+ if (g && g.contentWindow) {
+ results += "Failed to block creation of frame number " + (maxNumberOfFrames + 1) + ".";
+ } else {
+ results += "Successfully blocked creation of frame number " + (maxNumberOfFrames + 1) + ".";
+ }
+
+ document.getElementById("status").innerHTML = results;
+ }
+</script>
+</head>
+<body onLoad="createIFrames()">
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-images.html b/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-images.html
new file mode 100644
index 000000000..2233b8e0b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-images.html
@@ -0,0 +1,17 @@
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
+<img src="icon.png"/>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-text-vertical-lr.html b/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-text-vertical-lr.html
new file mode 100644
index 000000000..9598a3efa
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-text-vertical-lr.html
@@ -0,0 +1,3 @@
+<body style="-webkit-writing-mode: vertical-lr;">
+You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!
+</body>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-text.html b/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-text.html
new file mode 100644
index 000000000..3cd1b4c52
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/lots-of-text.html
@@ -0,0 +1,3 @@
+<body style="width:800px">
+You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too! You say it's your Birthday. It's my Birthday too!
+</body>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/modal-alerts-in-new-about-blank-window.html b/Tools/TestWebKitAPI/Tests/WebKit2/modal-alerts-in-new-about-blank-window.html
new file mode 100644
index 000000000..f76cb650b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/modal-alerts-in-new-about-blank-window.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+var newWindow = window.open("about:blank");
+newWindow.alert("Testing alert");
+newWindow.confirm("Testing confirm");
+newWindow.prompt("Testing prompt", "Default text");
+
+</script>
+</head>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/mouse-button-listener.html b/Tools/TestWebKitAPI/Tests/WebKit2/mouse-button-listener.html
new file mode 100644
index 000000000..1fdbb1a95
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/mouse-button-listener.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<script>
+ var mouseButton = -1;
+ var menuType = "none";
+
+ function mouseDownHandler(event)
+ {
+ mouseButton = event.button;
+ event.preventDefault();
+ }
+
+ function pressedMouseButton()
+ {
+ return mouseButton;
+ }
+
+ function contextMenuHandler(event)
+ {
+ menuType = "context";
+ event.preventDefault();
+ }
+
+ function displayedMenu()
+ {
+ return menuType;
+ }
+
+ addEventListener("mousedown", mouseDownHandler);
+ addEventListener("contextmenu", contextMenuHandler);
+</script>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/mouse-move-listener.html b/Tools/TestWebKitAPI/Tests/WebKit2/mouse-move-listener.html
new file mode 100644
index 000000000..afca7ed86
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/mouse-move-listener.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script>
+ var mouseMoved = false;
+
+ function mouseMoveHandler()
+ {
+ mouseMoved = true;
+ }
+
+ function didMoveMouse()
+ {
+ return mouseMoved;
+ }
+
+ addEventListener("mousemove", mouseMoveHandler);
+</script>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/open-and-close-window.html b/Tools/TestWebKitAPI/Tests/WebKit2/open-and-close-window.html
new file mode 100644
index 000000000..6ac778a9e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/open-and-close-window.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (document.location.search === "?close-window")
+ window.close();
+else
+ window.open("open-and-close-window.html?close-window");
+</script>
+</head>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/push-state.html b/Tools/TestWebKitAPI/Tests/WebKit2/push-state.html
new file mode 100644
index 000000000..f3a04a6bb
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/push-state.html
@@ -0,0 +1,3 @@
+<script type="text/javascript">
+history.pushState('newState', 0, '?newState');
+</script>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/should-open-external-schemes.html b/Tools/TestWebKitAPI/Tests/WebKit2/should-open-external-schemes.html
new file mode 100644
index 000000000..299e4d66a
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/should-open-external-schemes.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+function navigateToTelURL()
+{
+ window.location.href = "tel:+1 (408) 996-1010";
+}
+
+function navigateToTelURLInZeroTimer()
+{
+ window.setTimeout(navigateToTelURL, 0);
+}
+
+function navigateToTelURLInNestedZeroTimer()
+{
+ window.setTimeout(navigateToTelURLInZeroTimer, 0);
+}
+</script>
+</head>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/simple-accelerated-compositing.html b/Tools/TestWebKitAPI/Tests/WebKit2/simple-accelerated-compositing.html
new file mode 100644
index 000000000..bea62721b
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/simple-accelerated-compositing.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ <div style="-webkit-transform: translateZ(0);">Simple HTML file with accelerated compositing</div>
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/simple-form.html b/Tools/TestWebKitAPI/Tests/WebKit2/simple-form.html
new file mode 100644
index 000000000..3bf185293
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/simple-form.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+function submitForm()
+{
+ document.forms[0].submit();
+}
+</script>
+<form method=post>
+<input name=foo value="Some unimportant data">
+<input type=submit>
+</form>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/simple-iframe.html b/Tools/TestWebKitAPI/Tests/WebKit2/simple-iframe.html
new file mode 100644
index 000000000..429521c7f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/simple-iframe.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+ Simple HTML file.
+ <iframe src="simple.html"></iframe>
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/simple-tall.html b/Tools/TestWebKitAPI/Tests/WebKit2/simple-tall.html
new file mode 100644
index 000000000..a220e9b2d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/simple-tall.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+ Simple and tall HTML file.
+ <div style="height: 3000px;"></div>
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/simple.html b/Tools/TestWebKitAPI/Tests/WebKit2/simple.html
new file mode 100644
index 000000000..12cf87364
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/simple.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ Simple HTML file.
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/simple2.html b/Tools/TestWebKitAPI/Tests/WebKit2/simple2.html
new file mode 100644
index 000000000..1dcbfddd2
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/simple2.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ Second simple HTML file.
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/simple3.html b/Tools/TestWebKitAPI/Tests/WebKit2/simple3.html
new file mode 100644
index 000000000..786516e89
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/simple3.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ Third simple HTML file.
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/spacebar-scrolling.html b/Tools/TestWebKitAPI/Tests/WebKit2/spacebar-scrolling.html
new file mode 100644
index 000000000..8da08b3f9
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/spacebar-scrolling.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script>
+ function textFieldContainsSpace()
+ {
+ return document.querySelector("input").value === " ";
+ }
+
+ function blurTextField()
+ {
+ document.querySelector("input").blur();
+ }
+
+ function isDocumentScrolled()
+ {
+ return scrollY !== 0;
+ }
+
+ function loaded()
+ {
+ document.querySelector("input").focus();
+ }
+
+ addEventListener("load", loaded);
+</script>
+<input>
+<div style="height: 3000px;"></div>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/test-mse.mp4 b/Tools/TestWebKitAPI/Tests/WebKit2/test-mse.mp4
new file mode 100644
index 000000000..901c907e0
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/test-mse.mp4
Binary files differ
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2/test.mp4 b/Tools/TestWebKitAPI/Tests/WebKit2/test.mp4
new file mode 100644
index 000000000..d278c8ad8
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2/test.mp4
Binary files differ
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/AccessibilityTestServer.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/AccessibilityTestServer.cpp
new file mode 100644
index 000000000..165fcfb6e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/AccessibilityTestServer.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+static void loadChangedCallback(WebKitWebView*, WebKitLoadEvent loadEvent, gpointer)
+{
+ // Send a message to the parent process when we're ready.
+ if (loadEvent == WEBKIT_LOAD_FINISHED)
+ g_print("OK");
+}
+
+int main(int argc, char** argv)
+{
+ // Make sure that the ATK bridge is loaded.
+ g_setenv("GTK_MODULES", "atk-bridge", TRUE);
+
+ gtk_init(&argc, &argv);
+
+ WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
+ webkit_web_view_load_html(webView,
+ "<html>"
+ " <body>"
+ " <h1>This is a test</h1>"
+ " <p>This is a paragraph with some plain text.</p>"
+ " <p>This paragraph contains <a href=\"http://www.webkitgtk.org\">a link</a> in the middle.</p>"
+ " </body>"
+ "</html>",
+ 0);
+
+ GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(webView));
+ gtk_widget_show_all(window);
+
+ g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), 0);
+ g_signal_connect(webView, "load-changed", G_CALLBACK(loadChangedCallback), 0);
+
+ gtk_main();
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt
new file mode 100644
index 000000000..74d5250d4
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt
@@ -0,0 +1,128 @@
+set(TEST_LIBRARY_DIR ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/WebKit2GtkAPITests)
+set(TEST_BINARY_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/TestWebKitAPI/WebKit2Gtk)
+set(TEST_RESOURCES_DIR ${TEST_BINARY_DIR}/resources)
+file(MAKE_DIRECTORY ${TEST_RESOURCES_DIR})
+
+add_definitions(
+ -DWEBKIT_TEST_PLUGIN_DIR="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/plugins"
+ -DWEBKIT_EXEC_PATH="${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
+ -DWEBKIT_SRC_DIR="${CMAKE_SOURCE_DIR}"
+ -DWEBKIT_TEST_WEB_EXTENSIONS_DIR="${TEST_LIBRARY_DIR}"
+ -DWEBKIT_INJECTED_BUNDLE_PATH="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
+)
+
+include_directories(
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/Source
+ ${CMAKE_SOURCE_DIR}/Source/WTF
+ ${DERIVED_SOURCES_DIR}
+ ${DERIVED_SOURCES_WEBKIT2GTK_DIR}
+ ${FORWARDING_HEADERS_DIR}
+ ${FORWARDING_HEADERS_WEBKIT2GTK_DIR}
+ ${FORWARDING_HEADERS_WEBKIT2GTK_EXTENSION_DIR}
+ ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk
+)
+
+include_directories(SYSTEM
+ ${ATSPI_INCLUDE_DIRS}
+ ${GLIB_INCLUDE_DIRS}
+ ${GSTREAMER_INCLUDE_DIRS}
+ ${GTK3_INCLUDE_DIRS}
+ ${GTK_UNIX_PRINT_INCLUDE_DIRS}
+ ${LIBSOUP_INCLUDE_DIRS}
+)
+
+add_library(WebKit2APITestCore STATIC
+ ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk/LoadTrackingTest.cpp
+ ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestBus.cpp
+ ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestServer.cpp
+ ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.cpp
+ ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.cpp
+)
+target_link_libraries(WebKit2APITestCore WebKit2)
+
+add_custom_command(
+ OUTPUT ${TEST_RESOURCES_DIR}/webkit2gtk-tests-resources.gresource
+ DEPENDS resources/webkit2gtk-tests.gresource.xml
+ resources/link-title.js
+ COMMAND glib-compile-resources
+ --target=${TEST_RESOURCES_DIR}/webkit2gtk-tests-resources.gresource
+ --sourcedir=${CMAKE_SOURCE_DIR}
+ ${CMAKE_CURRENT_LIST_DIR}/resources/webkit2gtk-tests.gresource.xml
+)
+
+add_custom_target(test-gresource-bundle
+ DEPENDS ${TEST_RESOURCES_DIR}/webkit2gtk-tests-resources.gresource
+)
+
+macro(ADD_WK2_TEST_WEB_EXTENSION extension_name)
+ add_library(${extension_name} MODULE ${ARGN})
+ add_dependencies(${extension_name} WebKit2)
+ set_property(
+ TARGET ${extension_name}
+ APPEND
+ PROPERTY COMPILE_DEFINITIONS WEBKIT2_COMPILATION
+ )
+ set_target_properties(${extension_name} PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${TEST_LIBRARY_DIR}
+ )
+endmacro()
+
+macro(ADD_WK2_TEST test_name)
+ add_executable(${test_name} ${ARGN})
+ add_dependencies(${test_name}
+ test-gresource-bundle
+ WebExtensionTest
+ )
+ target_link_libraries(${test_name}
+ JavaScriptCore
+ WebKit2
+ WebKit2APITestCore
+ ${ATSPI_LIBRARIES}
+ ${GLIB_LIBRARIES}
+ ${GTK3_LIBRARIES}
+ ${GTK_UNIX_PRINT_LIBRARIES}
+ ${LIBSOUP_LIBRARIES}
+ )
+ set_target_properties(${test_name} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${TEST_BINARY_DIR}
+ )
+endmacro()
+
+ADD_WK2_TEST_WEB_EXTENSION(WebExtensionTest WebExtensionTest.cpp)
+ADD_WK2_TEST_WEB_EXTENSION(WebProcessTest DOMNodeTest.cpp DOMNodeFilterTest.cpp DOMXPathNSResolverTest.cpp FrameTest.cpp WebProcessTest.cpp EditorTest.cpp)
+
+ADD_WK2_TEST(InspectorTestServer InspectorTestServer.cpp)
+ADD_WK2_TEST(TestAuthentication TestAuthentication.cpp)
+ADD_WK2_TEST(TestBackForwardList TestBackForwardList.cpp)
+ADD_WK2_TEST(TestContextMenu TestContextMenu.cpp)
+ADD_WK2_TEST(TestCookieManager TestCookieManager.cpp)
+ADD_WK2_TEST(TestDOMNode TestDOMNode.cpp)
+ADD_WK2_TEST(TestDOMNodeFilter TestDOMNodeFilter.cpp)
+ADD_WK2_TEST(TestDOMXPathNSResolver TestDOMXPathNSResolver.cpp)
+ADD_WK2_TEST(TestDownloads TestDownloads.cpp)
+ADD_WK2_TEST(TestWebKitFaviconDatabase TestWebKitFaviconDatabase.cpp)
+ADD_WK2_TEST(TestWebKitFindController TestWebKitFindController.cpp)
+ADD_WK2_TEST(TestFrame TestFrame.cpp)
+ADD_WK2_TEST(TestInspector TestInspector.cpp)
+ADD_WK2_TEST(TestInspectorServer TestInspectorServer.cpp)
+ADD_WK2_TEST(TestLoaderClient TestLoaderClient.cpp)
+ADD_WK2_TEST(TestMultiprocess TestMultiprocess.cpp)
+ADD_WK2_TEST(TestPrinting TestPrinting.cpp)
+ADD_WK2_TEST(TestResources TestResources.cpp)
+ADD_WK2_TEST(TestSSL TestSSL.cpp)
+ADD_WK2_TEST(TestUIClient TestUIClient.cpp)
+ADD_WK2_TEST(TestWebExtensions TestWebExtensions.cpp)
+ADD_WK2_TEST(TestWebKitPolicyClient TestWebKitPolicyClient.cpp)
+ADD_WK2_TEST(TestWebKitSettings TestWebKitSettings.cpp)
+ADD_WK2_TEST(TestWebKitVersion TestWebKitVersion.cpp)
+ADD_WK2_TEST(TestWebViewEditor TestWebViewEditor.cpp)
+ADD_WK2_TEST(TestWebKitWebContext TestWebKitWebContext.cpp)
+ADD_WK2_TEST(TestWebKitWebView TestWebKitWebView.cpp)
+ADD_WK2_TEST(TestWebKitUserContentManager TestWebKitUserContentManager.cpp)
+ADD_WK2_TEST(TestEditor TestEditor.cpp)
+
+if (ATSPI_FOUND)
+ ADD_WK2_TEST(AccessibilityTestServer AccessibilityTestServer.cpp)
+ ADD_WK2_TEST(TestWebKitAccessibility TestWebKitAccessibility.cpp)
+endif ()
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMDOMWindowTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMDOMWindowTest.cpp
new file mode 100644
index 000000000..fc8cefb52
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMDOMWindowTest.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebProcessTest.h"
+#include <gio/gio.h>
+#include <webkit2/webkit-web-extension.h>
+#include <wtf/RunLoop.h>
+
+class WebKitDOMDOMWindowTest;
+static gboolean loadedCallback(WebKitDOMDOMWindow*, WebKitDOMEvent*, WebKitDOMDOMWindowTest*);
+static gboolean clickedCallback(WebKitDOMDOMWindow*, WebKitDOMEvent*, WebKitDOMDOMWindowTest*);
+
+class WebKitDOMDOMWindowTest : public WebProcessTest {
+public:
+ static std::unique_ptr<WebProcessTest> create() { return std::make_unique<WebKitDOMDOMWindowTest>(); }
+
+private:
+ guint64 webPageFromArgs(GVariant* args)
+ {
+ GVariantIter iter;
+ g_variant_iter_init(&iter, args);
+
+ const char* key;
+ GVariant* value;
+ while (g_variant_iter_loop(&iter, "{&sv}", &key, &value)) {
+ if (!strcmp(key, "pageID") && g_variant_classify(value) == G_VARIANT_CLASS_UINT64)
+ return g_variant_get_uint64(value);
+ }
+
+ g_assert_not_reached();
+ return 0;
+ }
+
+ bool testSignals(WebKitWebExtension* extension, GVariant* args)
+ {
+ notify("ready", "");
+
+ WebKitWebPage* page = webkit_web_extension_get_page(extension, webPageFromArgs(args));
+ g_assert(WEBKIT_IS_WEB_PAGE(page));
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+
+ WebKitDOMDOMWindow* domWindow = webkit_dom_document_get_default_view(document);
+ g_assert(domWindow);
+
+ // The "load" WebKitDOMDOMWindow signal is issued before this test is
+ // called. There's no way to capture it here. We simply assume that
+ // the document is loaded and notify the uiprocess accordingly
+ // notify("loaded", "");
+
+ webkit_dom_event_target_add_event_listener(
+ WEBKIT_DOM_EVENT_TARGET(domWindow),
+ "load",
+ G_CALLBACK(loadedCallback),
+ false,
+ this);
+
+ // loadedCallback() will stop this loop
+ RunLoop::run();
+
+ document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+
+ WebKitDOMElement* element = webkit_dom_document_get_element_by_id(document, "test");
+ g_assert(element);
+
+ webkit_dom_event_target_add_event_listener(
+ WEBKIT_DOM_EVENT_TARGET(element),
+ "click",
+ G_CALLBACK(clickedCallback),
+ false,
+ this);
+
+ // The "click" action will be issued in the uiprocess and that will
+ // trigger the dom event here.
+ // clickedCallback() will stop this loop
+ RunLoop::run();
+
+ return true;
+ }
+
+ bool testDispatchEvent(WebKitWebExtension* extension, GVariant* args)
+ {
+ notify("ready", "");
+
+ WebKitWebPage* page = webkit_web_extension_get_page(extension, webPageFromArgs(args));
+ g_assert(WEBKIT_IS_WEB_PAGE(page));
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+
+ WebKitDOMDOMWindow* domWindow = webkit_dom_document_get_default_view(document);
+ g_assert(domWindow);
+
+ // The "load" WebKitDOMDOMWindow signal is issued before this test is
+ // called. There's no way to capture it here. We simply assume that
+ // the document is loaded and notify the uiprocess accordingly
+ // notify("loaded", "");
+
+ webkit_dom_event_target_add_event_listener(
+ WEBKIT_DOM_EVENT_TARGET(domWindow),
+ "load",
+ G_CALLBACK(loadedCallback),
+ false,
+ this);
+
+ // loadedCallback() will stop this loop
+ RunLoop::run();
+
+ document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+
+ WebKitDOMElement* element = webkit_dom_document_get_element_by_id(document, "test");
+ g_assert(element);
+
+ WebKitDOMEvent* event = webkit_dom_document_create_event(document, "MouseEvent", 0);
+ g_assert(event);
+ g_assert(WEBKIT_DOM_IS_EVENT(event));
+ g_assert(WEBKIT_DOM_IS_MOUSE_EVENT(event));
+
+ glong clientX, clientY;
+ clientX = webkit_dom_element_get_client_left(element);
+ clientY = webkit_dom_element_get_client_top(element);
+
+ webkit_dom_event_target_add_event_listener(
+ WEBKIT_DOM_EVENT_TARGET(element),
+ "click",
+ G_CALLBACK(clickedCallback),
+ false,
+ this);
+
+ webkit_dom_mouse_event_init_mouse_event(WEBKIT_DOM_MOUSE_EVENT(event),
+ "click", TRUE, TRUE,
+ domWindow, 0, 0, 0, clientX, clientY,
+ FALSE, FALSE, FALSE, FALSE,
+ 1, WEBKIT_DOM_EVENT_TARGET(element));
+
+ webkit_dom_event_target_dispatch_event(WEBKIT_DOM_EVENT_TARGET(element), event, 0);
+
+ // clickedCallback() will stop this loop
+ RunLoop::run();
+
+ return true;
+ }
+
+ bool testGetComputedStyle(WebKitWebExtension* extension, GVariant* args)
+ {
+ WebKitWebPage* page = webkit_web_extension_get_page(extension, webPageFromArgs(args));
+ g_assert(WEBKIT_IS_WEB_PAGE(page));
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ WebKitDOMDOMWindow* domWindow = webkit_dom_document_get_default_view(document);
+ g_assert(domWindow);
+ WebKitDOMElement* element = webkit_dom_document_get_element_by_id(document, "test");
+ g_assert(element);
+ g_assert(WEBKIT_DOM_IS_ELEMENT(element));
+ WebKitDOMCSSStyleDeclaration* cssStyle = webkit_dom_dom_window_get_computed_style(domWindow, element, 0);
+ gchar* fontSize = webkit_dom_css_style_declaration_get_property_value(cssStyle, "font-size");
+ g_assert_cmpstr(fontSize, ==, "16px");
+
+ return true;
+ }
+
+ virtual bool runTest(const char* testName, WebKitWebExtension* extension, GVariant* args)
+ {
+ if (!strcmp(testName, "signals"))
+ return testSignals(extension, args);
+ if (!strcmp(testName, "dispatch-event"))
+ return testDispatchEvent(extension, args);
+ if (!strcmp(testName, "get-computed-style"))
+ return testGetComputedStyle(extension, args);
+
+ g_assert_not_reached();
+ return false;
+ }
+};
+
+static void __attribute__((constructor)) registerTests()
+{
+ REGISTER_TEST(WebKitDOMDOMWindowTest, "WebKitDOMDOMWindow/signals");
+ REGISTER_TEST(WebKitDOMDOMWindowTest, "WebKitDOMDOMWindow/dispatch-event");
+ REGISTER_TEST(WebKitDOMDOMWindowTest, "WebKitDOMDOMWindow/get-computed-style");
+}
+
+static gboolean loadedCallback(WebKitDOMDOMWindow* view, WebKitDOMEvent* event, WebKitDOMDOMWindowTest* test)
+{
+ test->notify("loaded", "");
+
+ // Stop the loop and let testSignals() or testDispatchEvent() continue its course
+ RunLoop::current().stop();
+
+ return FALSE;
+}
+
+static gboolean clickedCallback(WebKitDOMDOMWindow* view, WebKitDOMEvent* event, WebKitDOMDOMWindowTest* test)
+{
+ test->notify("clicked", "");
+ test->notify("finish", "");
+
+ // Stop the loop and let testSignals() or testDispatchEvent() continue its course
+ RunLoop::current().stop();
+
+ return FALSE;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp
new file mode 100644
index 000000000..0f18d1f31
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebProcessTest.h"
+#include <gio/gio.h>
+#include <webkit2/webkit-web-extension.h>
+#include <wtf/glib/GUniquePtr.h>
+
+typedef struct _WebKitNodeFilter {
+ GObject parent;
+} WebKitNodeFilter;
+
+typedef struct _WebKitNodeFilterClass {
+ GObjectClass parentClass;
+} WebKitNodeFilterClass;
+
+static short webkitNodeFilterAcceptNode(WebKitDOMNodeFilter*, WebKitDOMNode* node)
+{
+ // Filter out input elements.
+ return WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(node) ? WEBKIT_DOM_NODE_FILTER_REJECT : WEBKIT_DOM_NODE_FILTER_ACCEPT;
+}
+
+static void webkitNodeFilterDOMNodeFilterIfaceInit(WebKitDOMNodeFilterIface* iface)
+{
+ iface->accept_node = webkitNodeFilterAcceptNode;
+}
+
+G_DEFINE_TYPE_WITH_CODE(WebKitNodeFilter, webkit_node_filter, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(WEBKIT_DOM_TYPE_NODE_FILTER, webkitNodeFilterDOMNodeFilterIfaceInit))
+
+static void webkit_node_filter_init(WebKitNodeFilter*)
+{
+}
+
+static void webkit_node_filter_class_init(WebKitNodeFilterClass*)
+{
+}
+
+static const char* expectedNodesAll[] = { "HTML", "HEAD", "TITLE", "#text", "BODY", "INPUT", "INPUT", "BR" };
+static const char* expectedNodesNoInput[] = { "HTML", "HEAD", "TITLE", "#text", "BODY", "BR" };
+static const char* expectedElementsNoInput[] = { "HTML", "HEAD", "TITLE", "BODY", "BR" };
+
+class WebKitDOMNodeFilterTest : public WebProcessTest {
+public:
+ static std::unique_ptr<WebProcessTest> create() { return std::unique_ptr<WebProcessTest>(new WebKitDOMNodeFilterTest()); }
+
+private:
+ bool testTreeWalker(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
+
+ WebKitDOMElement* root = webkit_dom_document_get_element_by_id(document, "root");
+ g_assert(WEBKIT_DOM_IS_NODE(root));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(root));
+
+ // No filter.
+ GRefPtr<WebKitDOMTreeWalker> walker = adoptGRef(webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr));
+ g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(walker.get()));
+ g_assert(!webkit_dom_tree_walker_get_filter(walker.get()));
+
+ unsigned i = 0;
+ for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker.get()), ++i) {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedNodesAll));
+ GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node));
+ g_assert_cmpstr(nodeName.get(), ==, expectedNodesAll[i]);
+ }
+ g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedNodesAll));
+
+ // Input elements filter.
+ GRefPtr<WebKitDOMNodeFilter> filter = adoptGRef(static_cast<WebKitDOMNodeFilter*>(g_object_new(webkit_node_filter_get_type(), nullptr)));
+ walker = adoptGRef(webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, filter.get(), FALSE, nullptr));
+ g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(filter.get()));
+ g_assert(webkit_dom_tree_walker_get_filter(walker.get()) == filter.get());
+
+ i = 0;
+ for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker.get()), ++i) {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedNodesNoInput));
+ GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node));
+ g_assert_cmpstr(nodeName.get(), ==, expectedNodesNoInput[i]);
+ }
+ g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedNodesNoInput));
+
+ // Show only elements, reusing the input filter.
+ walker = adoptGRef(webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ELEMENT, filter.get(), FALSE, nullptr));
+ g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(walker.get()));
+ g_assert(webkit_dom_tree_walker_get_filter(walker.get()) == filter.get());
+
+ i = 0;
+ for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker.get()), ++i) {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedElementsNoInput));
+ GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node));
+ g_assert_cmpstr(nodeName.get(), ==, expectedElementsNoInput[i]);
+ }
+ g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedElementsNoInput));
+
+ return true;
+ }
+
+ bool testNodeIterator(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
+
+ WebKitDOMElement* root = webkit_dom_document_get_element_by_id(document, "root");
+ g_assert(WEBKIT_DOM_IS_NODE(root));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(root));
+
+ // No filter.
+ GRefPtr<WebKitDOMNodeIterator> iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr));
+ g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get()));
+ g_assert(!webkit_dom_node_iterator_get_filter(iter.get()));
+
+ unsigned i = 0;
+ while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter.get(), nullptr)) {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedNodesAll));
+ GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node));
+ g_assert_cmpstr(nodeName.get(), ==, expectedNodesAll[i]);
+ i++;
+ }
+ g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedNodesAll));
+
+ // Input elements filter.
+ GRefPtr<WebKitDOMNodeFilter> filter = adoptGRef(static_cast<WebKitDOMNodeFilter*>(g_object_new(webkit_node_filter_get_type(), nullptr)));
+ iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, filter.get(), FALSE, nullptr));
+ g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get()));
+ g_assert(webkit_dom_node_iterator_get_filter(iter.get()) == filter.get());
+
+ i = 0;
+ while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter.get(), nullptr)) {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedNodesNoInput));
+ GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node));
+ g_assert_cmpstr(nodeName.get(), ==, expectedNodesNoInput[i]);
+ i++;
+ }
+ g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedNodesNoInput));
+
+ // Show only elements, reusing the input filter.
+ iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ELEMENT, filter.get(), FALSE, nullptr));
+ g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get()));
+ g_assert(webkit_dom_node_iterator_get_filter(iter.get()) == filter.get());
+
+ i = 0;
+ while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter.get(), nullptr)) {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedElementsNoInput));
+ GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node));
+ g_assert_cmpstr(nodeName.get(), ==, expectedElementsNoInput[i]);
+ i++;
+ }
+ g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedElementsNoInput));
+
+ return true;
+ }
+
+ bool runTest(const char* testName, WebKitWebPage* page) override
+ {
+ if (!strcmp(testName, "tree-walker"))
+ return testTreeWalker(page);
+ if (!strcmp(testName, "node-iterator"))
+ return testNodeIterator(page);
+
+ g_assert_not_reached();
+ return false;
+ }
+};
+
+static void __attribute__((constructor)) registerTests()
+{
+ REGISTER_TEST(WebKitDOMNodeFilterTest, "WebKitDOMNodeFilter/tree-walker");
+ REGISTER_TEST(WebKitDOMNodeFilterTest, "WebKitDOMNodeFilter/node-iterator");
+}
+
+
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp
new file mode 100644
index 000000000..334384f67
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebProcessTest.h"
+#include <gio/gio.h>
+#include <webkit2/webkit-web-extension.h>
+#include <wtf/glib/GUniquePtr.h>
+
+class WebKitDOMNodeTest : public WebProcessTest {
+public:
+ static std::unique_ptr<WebProcessTest> create() { return std::unique_ptr<WebKitDOMNodeTest>(new WebKitDOMNodeTest()); }
+
+private:
+ bool testHierarchyNavigation(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
+
+ WebKitDOMHTMLHeadElement* head = webkit_dom_document_get_head(document);
+ g_assert(WEBKIT_DOM_IS_HTML_HEAD_ELEMENT(head));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(head));
+
+ // Title, head's child.
+ g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(head)));
+ GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(head)));
+ g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
+ g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
+ WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), 0);
+ g_assert(WEBKIT_DOM_IS_HTML_TITLE_ELEMENT(node));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+
+ // Body, Head sibling.
+ node = webkit_dom_node_get_next_sibling(WEBKIT_DOM_NODE(head));
+ g_assert(WEBKIT_DOM_IS_HTML_BODY_ELEMENT(node));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ WebKitDOMHTMLBodyElement* body = WEBKIT_DOM_HTML_BODY_ELEMENT(node);
+
+ // There is no third sibling
+ g_assert(!webkit_dom_node_get_next_sibling(node));
+
+ // Body's previous sibling is Head.
+ node = webkit_dom_node_get_previous_sibling(WEBKIT_DOM_NODE(body));
+ g_assert(WEBKIT_DOM_IS_HTML_HEAD_ELEMENT(node));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+
+ // Body has 3 children.
+ g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
+ list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
+ g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
+ unsigned long length = webkit_dom_node_list_get_length(list.get());
+ g_assert_cmpint(length, ==, 3);
+
+ // The three of them are P tags.
+ for (unsigned long i = 0; i < length; i++) {
+ node = webkit_dom_node_list_item(list.get(), i);
+ g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(node));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ }
+
+ // Go backwards
+ unsigned i;
+ for (i = 0; node; node = webkit_dom_node_get_previous_sibling(node), i++) { }
+ g_assert_cmpint(i, ==, 3);
+
+ return true;
+ }
+
+ bool testInsertion(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
+
+ WebKitDOMHTMLElement* body = webkit_dom_document_get_body(document);
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(body));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(body));
+
+ // Body shouldn't have any children at this point.
+ g_assert(!webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
+
+ // The value of a non-existent attribute should be null, not an empty string
+ g_assert(!webkit_dom_html_body_element_get_background(WEBKIT_DOM_HTML_BODY_ELEMENT(body)));
+
+ // Insert one P element.
+ WebKitDOMElement* p = webkit_dom_document_create_element(document, "P", 0);
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(p));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p));
+ webkit_dom_node_append_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(p), 0);
+
+ // Now it should have one, the same that we inserted.
+ g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
+ GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
+ g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
+ g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
+ WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), 0);
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(p), node));
+
+ // Replace the P tag with a DIV tag.
+ WebKitDOMElement* div = webkit_dom_document_create_element(document, "DIV", 0);
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div));
+ webkit_dom_node_replace_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(p), 0);
+ g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
+ list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
+ g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
+ g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
+ node = webkit_dom_node_list_item(list.get(), 0);
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));
+
+ // Now remove the tag.
+ webkit_dom_node_remove_child(WEBKIT_DOM_NODE(body), node, 0);
+ list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
+ g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
+ g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 0);
+
+ // Test insert before. If refChild is null, insert newChild as last element of parent.
+ div = webkit_dom_document_create_element(document, "DIV", 0);
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div));
+ webkit_dom_node_insert_before(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(div), 0, 0);
+ g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
+ list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
+ g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
+ g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
+ node = webkit_dom_node_list_item(list.get(), 0);
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));
+
+ // Now insert a 'p' before 'div'.
+ p = webkit_dom_document_create_element(document, "P", 0);
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(p));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p));
+ webkit_dom_node_insert_before(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(p), WEBKIT_DOM_NODE(div), 0);
+ g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
+ list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
+ g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
+ g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 2);
+ node = webkit_dom_node_list_item(list.get(), 0);
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(p), node));
+ node = webkit_dom_node_list_item(list.get(), 1);
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));
+
+ return true;
+ }
+
+ bool testTagNames(WebKitWebPage* page)
+ {
+ static const char* expectedTagNames[] = { "HTML", "HEAD", "BODY", "VIDEO", "SOURCE", "VIDEO", "SOURCE", "INPUT" };
+
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
+
+ GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_document_get_elements_by_tag_name(document, "*"));
+ g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
+ gulong nodeCount = webkit_dom_node_list_get_length(list.get());
+ g_assert_cmpuint(nodeCount, ==, G_N_ELEMENTS(expectedTagNames));
+ for (unsigned i = 0; i < nodeCount; i++) {
+ WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), i);
+ g_assert(WEBKIT_DOM_IS_NODE(node));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
+ GUniquePtr<char> tagName(webkit_dom_node_get_node_name(node));
+ g_assert_cmpstr(tagName.get(), ==, expectedTagNames[i]);
+ }
+
+ return true;
+ }
+
+ bool testDOMCache(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
+
+ // DOM objects already in the document should be automatically handled by the cache.
+ WebKitDOMElement* div = webkit_dom_document_get_element_by_id(document, "container");
+ g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div));
+
+ // Get the same elment twice should return the same pointer.
+ g_assert(div == webkit_dom_document_get_element_by_id(document, "container"));
+
+ // A new DOM object created that is derived from Node should be automatically handled by the cache.
+ WebKitDOMElement* p = webkit_dom_document_create_element(document, "P", nullptr);
+ g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p));
+
+ // A new DOM object created that isn't derived from Node should be manually handled.
+ GRefPtr<WebKitDOMNodeIterator> iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr));
+ g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get()));
+
+ // We can also manually handle a DOM object handled by the cache.
+ GRefPtr<WebKitDOMElement> p2 = adoptGRef(webkit_dom_document_create_element(document, "P", nullptr));
+ g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p2.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p2.get()));
+
+ // Manually handling a DOM object owned by the cache shouldn't crash when the cache has more than one reference.
+ GRefPtr<WebKitDOMElement> p3 = adoptGRef(webkit_dom_document_create_element(document, "P", nullptr));
+ g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p3.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p3.get()));
+ webkit_dom_node_append_child(WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(p3.get()), nullptr);
+
+ // DOM objects removed from the document are also correctly handled by the cache.
+ WebKitDOMElement* a = webkit_dom_document_create_element(document, "A", nullptr);
+ g_assert(WEBKIT_DOM_IS_ELEMENT(a));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(a));
+ webkit_dom_node_remove_child(WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(a), nullptr);
+
+ return true;
+ }
+
+ bool runTest(const char* testName, WebKitWebPage* page) override
+ {
+ if (!strcmp(testName, "hierarchy-navigation"))
+ return testHierarchyNavigation(page);
+ if (!strcmp(testName, "insertion"))
+ return testInsertion(page);
+ if (!strcmp(testName, "tag-names"))
+ return testTagNames(page);
+ if (!strcmp(testName, "dom-cache"))
+ return testDOMCache(page);
+
+ g_assert_not_reached();
+ return false;
+ }
+};
+
+static void __attribute__((constructor)) registerTests()
+{
+ REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/hierarchy-navigation");
+ REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/insertion");
+ REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/tag-names");
+ REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/dom-cache");
+}
+
+
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp
new file mode 100644
index 000000000..110677e41
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebProcessTest.h"
+#include <gio/gio.h>
+#include <webkit2/webkit-web-extension.h>
+#include <wtf/glib/GUniquePtr.h>
+
+typedef struct _WebKitXPathNSResolver {
+ GObject parent;
+} WebKitXPathNSResolver;
+
+typedef struct _WebKitXPathNSResolverClass {
+ GObjectClass parentClass;
+} WebKitXPathNSResolverClass;
+
+static char* webkitXPathNSResolverLookupNamespaceURI(WebKitDOMXPathNSResolver* resolver, const char* prefix)
+{
+ if (!g_strcmp0(prefix, "foo"))
+ return g_strdup("http://www.example.com");
+
+ return nullptr;
+}
+
+static void webkitXPathNSResolverDOMXPathNSResolverIfaceInit(WebKitDOMXPathNSResolverIface* iface)
+{
+ iface->lookup_namespace_uri = webkitXPathNSResolverLookupNamespaceURI;
+}
+
+G_DEFINE_TYPE_WITH_CODE(WebKitXPathNSResolver, webkit_xpath_ns_resolver, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(WEBKIT_DOM_TYPE_XPATH_NS_RESOLVER, webkitXPathNSResolverDOMXPathNSResolverIfaceInit))
+
+static void webkit_xpath_ns_resolver_init(WebKitXPathNSResolver*)
+{
+}
+
+static void webkit_xpath_ns_resolver_class_init(WebKitXPathNSResolverClass*)
+{
+}
+
+class WebKitDOMXPathNSResolverTest : public WebProcessTest {
+public:
+ static std::unique_ptr<WebProcessTest> create() { return std::unique_ptr<WebProcessTest>(new WebKitDOMXPathNSResolverTest()); }
+
+private:
+ void evaluateFooChildTextAndCheckResult(WebKitDOMDocument* document, WebKitDOMXPathNSResolver* resolver)
+ {
+ WebKitDOMElement* documentElement = webkit_dom_document_get_document_element(document);
+ g_assert(WEBKIT_DOM_IS_ELEMENT(documentElement));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(documentElement));
+
+ GRefPtr<WebKitDOMXPathResult> result = adoptGRef(webkit_dom_document_evaluate(document, "foo:child/text()", WEBKIT_DOM_NODE(documentElement), resolver, WEBKIT_DOM_XPATH_RESULT_ORDERED_NODE_ITERATOR_TYPE, nullptr, nullptr));
+ g_assert(WEBKIT_DOM_IS_XPATH_RESULT(result.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(result.get()));
+
+ WebKitDOMNode* nodeResult = webkit_dom_xpath_result_iterate_next(result.get(), nullptr);
+ g_assert(WEBKIT_DOM_IS_NODE(nodeResult));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(nodeResult));
+
+ GUniquePtr<char> nodeValue(webkit_dom_node_get_node_value(nodeResult));
+ g_assert_cmpstr(nodeValue.get(), ==, "SUCCESS");
+ }
+
+ bool testXPathNSResolverNative(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
+
+ GRefPtr<WebKitDOMXPathNSResolver> resolver = adoptGRef(webkit_dom_document_create_ns_resolver(document, WEBKIT_DOM_NODE(webkit_dom_document_get_document_element(document))));
+ g_assert(WEBKIT_DOM_IS_XPATH_NS_RESOLVER(resolver.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(resolver.get()));
+ evaluateFooChildTextAndCheckResult(document, resolver.get());
+
+ return true;
+ }
+
+ bool testXPathNSResolverCustom(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
+
+ GRefPtr<WebKitDOMXPathNSResolver> resolver = adoptGRef(WEBKIT_DOM_XPATH_NS_RESOLVER(g_object_new(webkit_xpath_ns_resolver_get_type(), nullptr)));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(resolver.get()));
+ evaluateFooChildTextAndCheckResult(document, resolver.get());
+
+ return true;
+ }
+
+ bool runTest(const char* testName, WebKitWebPage* page) override
+ {
+ if (!strcmp(testName, "native"))
+ return testXPathNSResolverNative(page);
+ if (!strcmp(testName, "custom"))
+ return testXPathNSResolverCustom(page);
+
+ g_assert_not_reached();
+ return false;
+ }
+};
+
+static void __attribute__((constructor)) registerTests()
+{
+ REGISTER_TEST(WebKitDOMXPathNSResolverTest, "WebKitDOMXPathNSResolver/native");
+ REGISTER_TEST(WebKitDOMXPathNSResolverTest, "WebKitDOMXPathNSResolver/custom");
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/EditorTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/EditorTest.cpp
new file mode 100644
index 000000000..873344271
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/EditorTest.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebProcessTest.h"
+#include <webkit2/webkit-web-extension.h>
+
+#define WEBKIT_DOM_USE_UNSTABLE_API
+#include <webkitdom/WebKitDOMDOMSelection.h>
+#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
+
+class WebKitWebEditorTest : public WebProcessTest {
+public:
+ static std::unique_ptr<WebProcessTest> create() { return std::unique_ptr<WebProcessTest>(new WebKitWebEditorTest()); }
+
+private:
+ static void selectionChangedCallback(bool* selectionChanged)
+ {
+ *selectionChanged = true;
+ }
+
+ void testSelectionSelectAll(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+
+ webkit_dom_document_exec_command(document, "SelectAll", false, "");
+ }
+
+ void testSelectionCollapse(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ GRefPtr<WebKitDOMDOMWindow> domWindow = adoptGRef(webkit_dom_document_get_default_view(document));
+ g_assert(WEBKIT_DOM_IS_DOM_WINDOW(domWindow.get()));
+ GRefPtr<WebKitDOMDOMSelection> domSelection = adoptGRef(webkit_dom_dom_window_get_selection(domWindow.get()));
+ g_assert(WEBKIT_DOM_IS_DOM_SELECTION(domSelection.get()));
+
+ webkit_dom_dom_selection_collapse_to_start(domSelection.get(), nullptr);
+ }
+
+ void testSelectionModifyMove(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ GRefPtr<WebKitDOMDOMWindow> domWindow = adoptGRef(webkit_dom_document_get_default_view(document));
+ g_assert(WEBKIT_DOM_IS_DOM_WINDOW(domWindow.get()));
+ GRefPtr<WebKitDOMDOMSelection> domSelection = adoptGRef(webkit_dom_dom_window_get_selection(domWindow.get()));
+ g_assert(WEBKIT_DOM_IS_DOM_SELECTION(domSelection.get()));
+
+ webkit_dom_dom_selection_modify(domSelection.get(), "move", "forward", "character");
+ }
+
+ void testSelectionModifyExtend(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+ GRefPtr<WebKitDOMDOMWindow> domWindow = adoptGRef(webkit_dom_document_get_default_view(document));
+ g_assert(WEBKIT_DOM_IS_DOM_WINDOW(domWindow.get()));
+ GRefPtr<WebKitDOMDOMSelection> domSelection = adoptGRef(webkit_dom_dom_window_get_selection(domWindow.get()));
+ g_assert(WEBKIT_DOM_IS_DOM_SELECTION(domSelection.get()));
+
+ webkit_dom_dom_selection_modify(domSelection.get(), "extend", "forward", "word");
+ }
+
+ void testSelectionUnselect(WebKitWebPage* page)
+ {
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ g_assert(WEBKIT_DOM_IS_DOCUMENT(document));
+
+ webkit_dom_document_exec_command(document, "Unselect", false, "");
+ }
+
+ bool runTest(const char* testName, WebKitWebPage* page) override
+ {
+ if (!strcmp(testName, "selection-changed")) {
+ bool selectionChanged = false;
+
+ WebKitWebEditor* editor = webkit_web_page_get_editor(page);
+ g_assert(WEBKIT_IS_WEB_EDITOR(editor));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(editor));
+ g_signal_connect_swapped(editor, "selection-changed", G_CALLBACK(selectionChangedCallback), &selectionChanged);
+
+ testSelectionSelectAll(page);
+ g_assert(selectionChanged);
+
+ selectionChanged = false;
+ testSelectionCollapse(page);
+ g_assert(selectionChanged);
+
+ selectionChanged = false;
+ testSelectionModifyMove(page);
+ g_assert(selectionChanged);
+
+ selectionChanged = false;
+ testSelectionModifyExtend(page);
+ g_assert(selectionChanged);
+
+ selectionChanged = false;
+ testSelectionUnselect(page);
+ g_assert(selectionChanged);
+
+ return true;
+ }
+
+ g_assert_not_reached();
+ return false;
+ }
+};
+
+static void __attribute__((constructor)) registerTests()
+{
+ REGISTER_TEST(WebKitWebEditorTest, "WebKitWebEditor/selection-changed");
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/FrameTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/FrameTest.cpp
new file mode 100644
index 000000000..a32d3e52f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/FrameTest.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebProcessTest.h"
+#include <gio/gio.h>
+#include <webkit2/webkit-web-extension.h>
+
+class WebKitFrameTest : public WebProcessTest {
+public:
+ static std::unique_ptr<WebProcessTest> create() { return std::unique_ptr<WebProcessTest>(new WebKitFrameTest()); }
+
+private:
+ bool testMainFrame(WebKitWebPage* page)
+ {
+ WebKitFrame* frame = webkit_web_page_get_main_frame(page);
+ g_assert(WEBKIT_IS_FRAME(frame));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(frame));
+ g_assert(webkit_frame_is_main_frame(frame));
+
+ return true;
+ }
+
+ bool testURI(WebKitWebPage* page)
+ {
+ WebKitFrame* frame = webkit_web_page_get_main_frame(page);
+ g_assert(WEBKIT_IS_FRAME(frame));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(frame));
+ g_assert_cmpstr(webkit_web_page_get_uri(page), ==, webkit_frame_get_uri(frame));
+
+ return true;
+ }
+
+ bool testJavaScriptContext(WebKitWebPage* page)
+ {
+ WebKitFrame* frame = webkit_web_page_get_main_frame(page);
+ g_assert(WEBKIT_IS_FRAME(frame));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(frame));
+ g_assert(webkit_frame_get_javascript_global_context(frame));
+
+ return true;
+ }
+
+ bool runTest(const char* testName, WebKitWebPage* page) override
+ {
+ if (!strcmp(testName, "main-frame"))
+ return testMainFrame(page);
+ if (!strcmp(testName, "uri"))
+ return testURI(page);
+ if (!strcmp(testName, "javascript-context"))
+ return testJavaScriptContext(page);
+
+ g_assert_not_reached();
+ return false;
+ }
+};
+
+static void __attribute__((constructor)) registerTests()
+{
+ REGISTER_TEST(WebKitFrameTest, "WebKitFrame/main-frame");
+ REGISTER_TEST(WebKitFrameTest, "WebKitFrame/uri");
+ REGISTER_TEST(WebKitFrameTest, "WebKitFrame/javascript-context");
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/InspectorTestServer.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/InspectorTestServer.cpp
new file mode 100644
index 000000000..f13b043df
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/InspectorTestServer.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Ltd. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+static void loadChangedCallback(WebKitWebView*, WebKitLoadEvent loadEvent, gpointer)
+{
+ // Send a message to the parent process when we're ready.
+ if (loadEvent == WEBKIT_LOAD_FINISHED)
+ g_print("OK");
+}
+
+int main(int argc, char** argv)
+{
+ gtk_init(&argc, &argv);
+
+ // Overwrite WEBKIT_INSPECTOR_SERVER variable with default value.
+ g_setenv("WEBKIT_INSPECTOR_SERVER", "127.0.0.1:2999", TRUE);
+
+ WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
+ webkit_settings_set_enable_developer_extras(webkit_web_view_get_settings(webView), TRUE);
+ webkit_web_view_load_html(webView,
+ "<html><body><p>WebKitGTK+ Inspector Test Server</p></body></html>",
+ "http://127.0.0.1:2999/");
+
+ GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(webView));
+ gtk_widget_show_all(window);
+
+ g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), 0);
+ g_signal_connect(webView, "load-changed", G_CALLBACK(loadChangedCallback), 0);
+
+ gtk_main();
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAuthentication.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAuthentication.cpp
new file mode 100644
index 000000000..0d0e0765d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAuthentication.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "LoadTrackingTest.h"
+#include "WebKitTestServer.h"
+#include <wtf/glib/GRefPtr.h>
+
+static WebKitTestServer* kServer;
+
+class AuthenticationTest: public LoadTrackingTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(AuthenticationTest);
+
+ AuthenticationTest()
+ {
+ g_signal_connect(m_webView, "authenticate", G_CALLBACK(runAuthenticationCallback), this);
+ }
+
+ ~AuthenticationTest()
+ {
+ g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ static int authenticationRetries;
+ static bool authenticationCancelledReceived;
+
+ void loadURI(const char* uri)
+ {
+ // Reset the retry count of the fake server when a page is loaded.
+ authenticationRetries = 0;
+ authenticationCancelledReceived = false;
+ LoadTrackingTest::loadURI(uri);
+ }
+
+ static gboolean runAuthenticationCallback(WebKitWebView*, WebKitAuthenticationRequest* request, AuthenticationTest* test)
+ {
+ g_signal_connect(request, "cancelled", G_CALLBACK(authenticationCancelledCallback), test);
+ test->runAuthentication(request);
+ return TRUE;
+ }
+
+ static void authenticationCancelledCallback(WebKitAuthenticationRequest*, AuthenticationTest*)
+ {
+ authenticationCancelledReceived = true;
+ }
+
+ void runAuthentication(WebKitAuthenticationRequest* request)
+ {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
+ m_authenticationRequest = request;
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ WebKitAuthenticationRequest* waitForAuthenticationRequest()
+ {
+ g_main_loop_run(m_mainLoop);
+ return m_authenticationRequest.get();
+ }
+
+private:
+ GRefPtr<WebKitAuthenticationRequest> m_authenticationRequest;
+};
+
+int AuthenticationTest::authenticationRetries = 0;
+bool AuthenticationTest::authenticationCancelledReceived = false;
+
+static const char authTestUsername[] = "username";
+static const char authTestPassword[] = "password";
+static const char authExpectedSuccessTitle[] = "WebKit2Gtk+ Authentication test";
+static const char authExpectedFailureTitle[] = "401 Authorization Required";
+static const char authExpectedAuthorization[] = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="; // Base64 encoding of "username:password".
+static const char authSuccessHTMLString[] =
+ "<html>"
+ "<head><title>WebKit2Gtk+ Authentication test</title></head>"
+ "<body></body></html>";
+static const char authFailureHTMLString[] =
+ "<html>"
+ "<head><title>401 Authorization Required</title></head>"
+ "<body></body></html>";
+
+static void testWebViewAuthenticationRequest(AuthenticationTest* test, gconstpointer)
+{
+ // Test authentication request getters match soup authentication header.
+ test->loadURI(kServer->getURIForPath("/auth-test.html").data());
+ WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+ g_assert_cmpstr(webkit_authentication_request_get_host(request), ==, soup_uri_get_host(kServer->baseURI()));
+ g_assert_cmpuint(webkit_authentication_request_get_port(request), ==, soup_uri_get_port(kServer->baseURI()));
+ g_assert_cmpstr(webkit_authentication_request_get_realm(request), ==, "my realm");
+ g_assert(webkit_authentication_request_get_scheme(request) == WEBKIT_AUTHENTICATION_SCHEME_HTTP_BASIC);
+ g_assert(!webkit_authentication_request_is_for_proxy(request));
+ g_assert(!webkit_authentication_request_is_retry(request));
+}
+
+static void testWebViewAuthenticationCancel(AuthenticationTest* test, gconstpointer)
+{
+ // Test cancel.
+ test->loadURI(kServer->getURIForPath("/auth-test.html").data());
+ WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+ webkit_authentication_request_cancel(request);
+ // Server doesn't ask for new credentials.
+ test->waitUntilLoadFinished();
+
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::ProvisionalLoadFailed);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+
+ g_assert_error(test->m_error.get(), WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_CANCELLED);
+}
+
+static void testWebViewAuthenticationLoadCancelled(AuthenticationTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/auth-test.html").data());
+ test->waitForAuthenticationRequest();
+ webkit_web_view_stop_loading(test->m_webView);
+ // Expect empty page.
+ test->waitUntilLoadFinished();
+ g_assert(test->authenticationCancelledReceived);
+
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::ProvisionalLoadFailed);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+
+ g_assert_error(test->m_error.get(), WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_CANCELLED);
+}
+
+static void testWebViewAuthenticationFailure(AuthenticationTest* test, gconstpointer)
+{
+ // Test authentication failures.
+ test->loadURI(kServer->getURIForPath("/auth-test.html").data());
+ WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+ g_assert(!webkit_authentication_request_is_retry(request));
+ WebKitCredential* credential = webkit_credential_new(authTestUsername, "wrongpassword", WEBKIT_CREDENTIAL_PERSISTENCE_NONE);
+ webkit_authentication_request_authenticate(request, credential);
+ webkit_credential_free(credential);
+ // Expect a second authentication request.
+ request = test->waitForAuthenticationRequest();
+ g_assert(webkit_authentication_request_is_retry(request));
+ // Test second failure.
+ credential = webkit_credential_new(authTestUsername, "wrongpassword2", WEBKIT_CREDENTIAL_PERSISTENCE_NONE);
+ webkit_authentication_request_authenticate(request, credential);
+ webkit_credential_free(credential);
+ // Expect authentication failed page.
+ test->waitUntilLoadFinished();
+
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+ g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, authExpectedFailureTitle);
+}
+
+static void testWebViewAuthenticationNoCredential(AuthenticationTest* test, gconstpointer)
+{
+ // Test continue without credentials.
+ test->loadURI(kServer->getURIForPath("/auth-test.html").data());
+ WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+ webkit_authentication_request_authenticate(request, 0);
+ // Server doesn't ask for new credentials.
+ test->waitUntilLoadFinished();
+
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+ g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, authExpectedFailureTitle);
+}
+
+static void testWebViewAuthenticationStorage(AuthenticationTest* test, gconstpointer)
+{
+ // Enable private browsing before authentication request to test that credentials can't be saved.
+ webkit_settings_set_enable_private_browsing(webkit_web_view_get_settings(test->m_webView), TRUE);
+ test->loadURI(kServer->getURIForPath("/auth-test.html").data());
+ WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+ g_assert(!webkit_authentication_request_get_proposed_credential(request));
+ g_assert(!webkit_authentication_request_can_save_credentials(request));
+
+ // If WebKit has been compiled with libsecret, and private browsing is disabled
+ // then check that credentials can be saved.
+#if ENABLE(CREDENTIAL_STORAGE)
+ webkit_settings_set_enable_private_browsing(webkit_web_view_get_settings(test->m_webView), FALSE);
+ test->loadURI(kServer->getURIForPath("/auth-test.html").data());
+ request = test->waitForAuthenticationRequest();
+ g_assert(!webkit_authentication_request_get_proposed_credential(request));
+ g_assert(webkit_authentication_request_can_save_credentials(request));
+#endif
+}
+
+static void testWebViewAuthenticationSuccess(AuthenticationTest* test, gconstpointer)
+{
+ // Test correct authentication.
+ test->loadURI(kServer->getURIForPath("/auth-test.html").data());
+ WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+ WebKitCredential* credential = webkit_credential_new(authTestUsername, authTestPassword, WEBKIT_CREDENTIAL_PERSISTENCE_FOR_SESSION);
+ webkit_authentication_request_authenticate(request, credential);
+ webkit_credential_free(credential);
+ test->waitUntilLoadFinished();
+
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+ g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, authExpectedSuccessTitle);
+
+ // Test loading the same (authorized) page again.
+ test->loadURI(kServer->getURIForPath("/auth-test.html").data());
+ // There is no authentication challenge.
+ test->waitUntilLoadFinished();
+
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+ g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, authExpectedSuccessTitle);
+}
+
+static void testWebViewAuthenticationEmptyRealm(AuthenticationTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/empty-realm.html").data());
+ WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+ WebKitCredential* credential = webkit_credential_new(authTestUsername, authTestPassword, WEBKIT_CREDENTIAL_PERSISTENCE_FOR_SESSION);
+ webkit_authentication_request_authenticate(request, credential);
+ webkit_credential_free(credential);
+ test->waitUntilLoadFinished();
+
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+ g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, authExpectedSuccessTitle);
+}
+
+static void serverCallback(SoupServer*, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, void*)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (!strcmp(path, "/auth-test.html") || !strcmp(path, "/empty-realm.html")) {
+ const char* authorization = soup_message_headers_get_one(message->request_headers, "Authorization");
+ // Require authentication.
+ if (!g_strcmp0(authorization, authExpectedAuthorization)) {
+ // Successful authentication.
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, authSuccessHTMLString, strlen(authSuccessHTMLString));
+ AuthenticationTest::authenticationRetries = 0;
+ } else if (++AuthenticationTest::authenticationRetries < 3) {
+ // No or invalid authorization header provided by the client, request authentication twice then fail.
+ soup_message_set_status(message, SOUP_STATUS_UNAUTHORIZED);
+ if (!strcmp(path, "/empty-realm.html"))
+ soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic");
+ else
+ soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic realm=\"my realm\"");
+ // Include a failure message in case the user attempts to proceed without authentication.
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, authFailureHTMLString, strlen(authFailureHTMLString));
+ } else {
+ // Authentication not successful, display a "401 Authorization Required" page.
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, authFailureHTMLString, strlen(authFailureHTMLString));
+ }
+ } else
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+
+ soup_message_body_complete(message->response_body);
+}
+
+void beforeAll()
+{
+ kServer = new WebKitTestServer();
+ kServer->run(serverCallback);
+
+ AuthenticationTest::add("WebKitWebView", "authentication-request", testWebViewAuthenticationRequest);
+ AuthenticationTest::add("WebKitWebView", "authentication-cancel", testWebViewAuthenticationCancel);
+ AuthenticationTest::add("WebKitWebView", "authentication-load-cancelled", testWebViewAuthenticationLoadCancelled);
+ AuthenticationTest::add("WebKitWebView", "authentication-success", testWebViewAuthenticationSuccess);
+ AuthenticationTest::add("WebKitWebView", "authentication-failure", testWebViewAuthenticationFailure);
+ AuthenticationTest::add("WebKitWebView", "authentication-no-credential", testWebViewAuthenticationNoCredential);
+ AuthenticationTest::add("WebKitWebView", "authentication-storage", testWebViewAuthenticationStorage);
+ AuthenticationTest::add("WebKitWebView", "authentication-empty-realm", testWebViewAuthenticationEmptyRealm);
+}
+
+void afterAll()
+{
+ delete kServer;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestBackForwardList.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestBackForwardList.cpp
new file mode 100644
index 000000000..65c3d2d7c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestBackForwardList.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebKitTestServer.h"
+#include "WebViewTest.h"
+#include <gtk/gtk.h>
+#include <libsoup/soup.h>
+#include <string.h>
+#include <webkit2/webkit2.h>
+
+// Back forward list limit is 100 by default.
+static const int kBackForwardListLimit = 100;
+
+static WebKitTestServer* kServer;
+
+static void serverCallback(SoupServer* server, SoupMessage* msg, const char* path, GHashTable* query, SoupClientContext* context, gpointer data)
+{
+ if (msg->method != SOUP_METHOD_GET) {
+ soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (g_str_has_suffix(path, "favicon.ico")) {
+ soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
+ return;
+ }
+
+ soup_message_set_status(msg, SOUP_STATUS_OK);
+
+ char* body = g_strdup_printf("<html><title>%s</title><body>%s</body></html>", path + 1, path + 1);
+ soup_message_body_append(msg->response_body, SOUP_MEMORY_TAKE, body, strlen(body));
+
+ soup_message_body_complete(msg->response_body);
+}
+
+class BackForwardListTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(BackForwardListTest);
+
+ enum {
+ Backward,
+ Forward
+ };
+
+ enum {
+ CurrentItem = 1 << 0,
+ AddedItem = 1 << 1,
+ RemovedItems = 1 << 2
+ };
+
+ static void checkItem(WebKitBackForwardListItem* item, const char* title, const char* uri, const char* originalURI)
+ {
+ g_assert(item);
+ g_assert_cmpstr(webkit_back_forward_list_item_get_uri(item), ==, uri);
+ g_assert_cmpstr(webkit_back_forward_list_item_get_title(item), == , title);
+ g_assert_cmpstr(webkit_back_forward_list_item_get_original_uri(item), ==, originalURI);
+ }
+
+ static void checkItemIndex(WebKitBackForwardList* list)
+ {
+ g_assert(webkit_back_forward_list_get_nth_item(list, -1) == webkit_back_forward_list_get_back_item(list));
+ g_assert(webkit_back_forward_list_get_nth_item(list, 0) == webkit_back_forward_list_get_current_item(list));
+ g_assert(webkit_back_forward_list_get_nth_item(list, 1) == webkit_back_forward_list_get_forward_item(list));
+ }
+
+ static void checkList(WebKitBackForwardList* list, unsigned type, WebKitBackForwardListItem** items, unsigned nItems)
+ {
+ GList* listItems = type == BackForwardListTest::Backward ? webkit_back_forward_list_get_back_list(list) :
+ webkit_back_forward_list_get_forward_list(list);
+ g_assert(listItems);
+
+ unsigned i = 0;
+ for (GList* listItem = listItems; listItem; listItem = g_list_next(listItem), i++) {
+ g_assert_cmpuint(i, <, nItems);
+ g_assert(listItem->data == items[i]);
+ }
+ g_list_free(listItems);
+ }
+
+ static void backForwardListChanged(WebKitBackForwardList* list, WebKitBackForwardListItem* addedItem, GList* removedItems, BackForwardListTest* test)
+ {
+ test->m_hasChanged = true;
+
+ if (test->m_changedFlags & BackForwardListTest::AddedItem) {
+ g_assert(WEBKIT_IS_BACK_FORWARD_LIST_ITEM(addedItem));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(addedItem));
+ } else
+ g_assert(!addedItem);
+
+ if (test->m_changedFlags & BackForwardListTest::RemovedItems) {
+ g_assert(removedItems);
+ for (GList* iter = removedItems; iter; iter = iter->next) {
+ g_assert(WEBKIT_IS_BACK_FORWARD_LIST_ITEM(iter->data));
+ if (test->m_expectedRemovedItems)
+ g_assert(g_list_find(test->m_expectedRemovedItems, iter->data));
+ }
+
+ } else
+ g_assert(!removedItems);
+ }
+
+ BackForwardListTest()
+ : m_list(webkit_web_view_get_back_forward_list(m_webView))
+ , m_changedFlags(0)
+ , m_hasChanged(false)
+ , m_expectedRemovedItems(0)
+ {
+ g_signal_connect(m_list, "changed", G_CALLBACK(backForwardListChanged), this);
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_list));
+ }
+
+ ~BackForwardListTest()
+ {
+ g_signal_handlers_disconnect_matched(m_list, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ void waitUntilLoadFinished()
+ {
+ m_hasChanged = false;
+ WebViewTest::waitUntilLoadFinished();
+ g_assert(m_hasChanged);
+ }
+
+ void waitUntilLoadFinishedAndCheckRemovedItems(GList* removedItems)
+ {
+ m_expectedRemovedItems = removedItems;
+ waitUntilLoadFinished();
+ m_expectedRemovedItems = 0;
+ }
+
+ WebKitBackForwardList* m_list;
+ unsigned long m_changedFlags;
+ bool m_hasChanged;
+ GList* m_expectedRemovedItems;
+};
+
+static void testBackForwardListNavigation(BackForwardListTest* test, gconstpointer)
+{
+ WebKitBackForwardListItem* items[1];
+
+ g_assert(!webkit_web_view_can_go_back(test->m_webView));
+ g_assert(!webkit_web_view_can_go_forward(test->m_webView));
+
+ g_assert_cmpuint(webkit_back_forward_list_get_length(test->m_list), ==, 0);
+ g_assert(!webkit_back_forward_list_get_current_item(test->m_list));
+ g_assert(!webkit_back_forward_list_get_back_item(test->m_list));
+ g_assert(!webkit_back_forward_list_get_forward_item(test->m_list));
+ BackForwardListTest::checkItemIndex(test->m_list);
+ g_assert(!webkit_back_forward_list_get_back_list(test->m_list));
+ g_assert(!webkit_back_forward_list_get_forward_list(test->m_list));
+
+ CString uriPage1 = kServer->getURIForPath("/Page1");
+ test->m_changedFlags = BackForwardListTest::CurrentItem | BackForwardListTest::AddedItem;
+ test->loadURI(uriPage1.data());
+ test->waitUntilLoadFinished();
+
+ g_assert(!webkit_web_view_can_go_back(test->m_webView));
+ g_assert(!webkit_web_view_can_go_forward(test->m_webView));
+
+ g_assert_cmpuint(webkit_back_forward_list_get_length(test->m_list), ==, 1);
+ WebKitBackForwardListItem* itemPage1 = webkit_back_forward_list_get_current_item(test->m_list);
+ BackForwardListTest::checkItem(itemPage1, "Page1", uriPage1.data(), uriPage1.data());
+ g_assert(!webkit_back_forward_list_get_back_item(test->m_list));
+ g_assert(!webkit_back_forward_list_get_forward_item(test->m_list));
+ BackForwardListTest::checkItemIndex(test->m_list);
+ g_assert(!webkit_back_forward_list_get_back_list(test->m_list));
+ g_assert(!webkit_back_forward_list_get_forward_list(test->m_list));
+
+ CString uriPage2 = kServer->getURIForPath("/Page2");
+ test->m_changedFlags = BackForwardListTest::CurrentItem | BackForwardListTest::AddedItem;
+ test->loadURI(uriPage2.data());
+ test->waitUntilLoadFinished();
+
+ g_assert(webkit_web_view_can_go_back(test->m_webView));
+ g_assert(!webkit_web_view_can_go_forward(test->m_webView));
+
+ g_assert_cmpuint(webkit_back_forward_list_get_length(test->m_list), ==, 2);
+ WebKitBackForwardListItem* itemPage2 = webkit_back_forward_list_get_current_item(test->m_list);
+ BackForwardListTest::checkItem(itemPage2, "Page2", uriPage2.data(), uriPage2.data());
+ g_assert(webkit_back_forward_list_get_back_item(test->m_list) == itemPage1);
+ g_assert(!webkit_back_forward_list_get_forward_item(test->m_list));
+ BackForwardListTest::checkItemIndex(test->m_list);
+ items[0] = itemPage1;
+ BackForwardListTest::checkList(test->m_list, BackForwardListTest::Backward, items, 1);
+ g_assert(!webkit_back_forward_list_get_forward_list(test->m_list));
+
+ test->m_changedFlags = BackForwardListTest::CurrentItem;
+ test->goBack();
+ test->waitUntilLoadFinished();
+
+ g_assert(!webkit_web_view_can_go_back(test->m_webView));
+ g_assert(webkit_web_view_can_go_forward(test->m_webView));
+
+ g_assert_cmpuint(webkit_back_forward_list_get_length(test->m_list), ==, 2);
+ g_assert(itemPage1 == webkit_back_forward_list_get_current_item(test->m_list));
+ BackForwardListTest::checkItem(webkit_back_forward_list_get_current_item(test->m_list), "Page1", uriPage1.data(), uriPage1.data());
+ g_assert(!webkit_back_forward_list_get_back_item(test->m_list));
+ g_assert(webkit_back_forward_list_get_forward_item(test->m_list) == itemPage2);
+ BackForwardListTest::checkItemIndex(test->m_list);
+ g_assert(!webkit_back_forward_list_get_back_list(test->m_list));
+ items[0] = itemPage2;
+ BackForwardListTest::checkList(test->m_list, BackForwardListTest::Forward, items, 1);
+
+ test->m_changedFlags = BackForwardListTest::CurrentItem;
+ test->goForward();
+ test->waitUntilLoadFinished();
+
+ g_assert(webkit_web_view_can_go_back(test->m_webView));
+ g_assert(!webkit_web_view_can_go_forward(test->m_webView));
+
+ g_assert_cmpuint(webkit_back_forward_list_get_length(test->m_list), ==, 2);
+ g_assert(itemPage2 == webkit_back_forward_list_get_current_item(test->m_list));
+ BackForwardListTest::checkItem(webkit_back_forward_list_get_current_item(test->m_list), "Page2", uriPage2.data(), uriPage2.data());
+ g_assert(webkit_back_forward_list_get_back_item(test->m_list) == itemPage1);
+ g_assert(!webkit_back_forward_list_get_forward_item(test->m_list));
+ BackForwardListTest::checkItemIndex(test->m_list);
+ items[0] = itemPage1;
+ BackForwardListTest::checkList(test->m_list, BackForwardListTest::Backward, items, 1);
+ g_assert(!webkit_back_forward_list_get_forward_list(test->m_list));
+
+ test->m_changedFlags = BackForwardListTest::CurrentItem;
+ test->goToBackForwardListItem(itemPage1);
+ test->waitUntilLoadFinished();
+
+ g_assert(itemPage1 == webkit_back_forward_list_get_current_item(test->m_list));
+}
+
+static void testBackForwardListLimitAndCache(BackForwardListTest* test, gconstpointer)
+{
+ for (int i = 0; i < kBackForwardListLimit; i++) {
+ GUniquePtr<char> path(g_strdup_printf("/Page%d", i));
+ test->m_changedFlags = BackForwardListTest::CurrentItem | BackForwardListTest::AddedItem;
+ test->loadURI(kServer->getURIForPath(path.get()).data());
+ test->waitUntilLoadFinished();
+ }
+
+ g_assert_cmpuint(webkit_back_forward_list_get_length(test->m_list), ==, kBackForwardListLimit);
+ WebKitBackForwardListItem* itemPageFirst = webkit_back_forward_list_get_nth_item(test->m_list, -(kBackForwardListLimit - 1));
+ GUniquePtr<GList> removedItems(g_list_prepend(0, itemPageFirst));
+
+ GUniquePtr<char> path(g_strdup_printf("/Page%d", kBackForwardListLimit));
+ test->m_changedFlags = BackForwardListTest::CurrentItem | BackForwardListTest::AddedItem | BackForwardListTest::RemovedItems;
+ test->loadURI(kServer->getURIForPath(path.get()).data());
+ test->waitUntilLoadFinishedAndCheckRemovedItems(removedItems.get());
+
+ g_assert_cmpuint(webkit_back_forward_list_get_length(test->m_list), ==, kBackForwardListLimit);
+}
+
+void beforeAll()
+{
+ kServer = new WebKitTestServer();
+ kServer->run(serverCallback);
+
+ BackForwardListTest::add("BackForwardList", "navigation", testBackForwardListNavigation);
+ BackForwardListTest::add("BackForwardList", "list-limit-and-cache", testBackForwardListLimitAndCache);
+}
+
+void afterAll()
+{
+ delete kServer;
+}
+
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestContextMenu.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestContextMenu.cpp
new file mode 100644
index 000000000..f2f0d6a57
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestContextMenu.cpp
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebViewTest.h"
+#include <wtf/Vector.h>
+#include <wtf/glib/GRefPtr.h>
+
+class ContextMenuTest: public WebViewTest {
+public:
+ enum ContextMenuItemStateFlags {
+ Visible = 1 << 0,
+ Enabled = 1 << 1,
+ Checked = 1 << 2
+ };
+
+ void checkContextMenuEvent(GdkEvent* event)
+ {
+ g_assert(event);
+ g_assert_cmpint(event->type, ==, GDK_BUTTON_PRESS);
+ g_assert_cmpint(event->button.button, ==, 3);
+ g_assert_cmpint(event->button.x, ==, m_menuPositionX);
+ g_assert_cmpint(event->button.y, ==, m_menuPositionY);
+ }
+
+ static gboolean contextMenuCallback(WebKitWebView* webView, WebKitContextMenu* contextMenu, GdkEvent* event, WebKitHitTestResult* hitTestResult, ContextMenuTest* test)
+ {
+ g_assert(WEBKIT_IS_CONTEXT_MENU(contextMenu));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(contextMenu));
+ test->checkContextMenuEvent(event);
+ g_assert(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(hitTestResult));
+
+ return test->contextMenu(contextMenu, event, hitTestResult);
+ }
+
+ static void contextMenuDismissedCallback(WebKitWebView*, ContextMenuTest* test)
+ {
+ test->contextMenuDismissed();
+ }
+
+ ContextMenuTest()
+ : m_menuPositionX(0)
+ , m_menuPositionY(0)
+ {
+ g_signal_connect(m_webView, "context-menu", G_CALLBACK(contextMenuCallback), this);
+ g_signal_connect(m_webView, "context-menu-dismissed", G_CALLBACK(contextMenuDismissedCallback), this);
+ }
+
+ ~ContextMenuTest()
+ {
+ g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ virtual bool contextMenu(WebKitContextMenu*, GdkEvent*, WebKitHitTestResult*) = 0;
+
+ virtual void contextMenuDismissed()
+ {
+ quitMainLoop();
+ }
+
+ GtkMenu* getPopupMenu()
+ {
+ GUniquePtr<GList> toplevels(gtk_window_list_toplevels());
+ for (GList* iter = toplevels.get(); iter; iter = g_list_next(iter)) {
+ if (!GTK_IS_WINDOW(iter->data))
+ continue;
+
+ GtkWidget* child = gtk_bin_get_child(GTK_BIN(iter->data));
+ if (!GTK_IS_MENU(child))
+ continue;
+
+ if (gtk_menu_get_attach_widget(GTK_MENU(child)) == GTK_WIDGET(m_webView))
+ return GTK_MENU(child);
+ }
+ g_assert_not_reached();
+ return 0;
+ }
+
+ bool shouldShowInputMethodsMenu()
+ {
+ GtkSettings* settings = gtk_widget_get_settings(GTK_WIDGET(m_webView));
+ if (!settings)
+ return true;
+
+ gboolean showInputMethodMenu;
+ g_object_get(settings, "gtk-show-input-method-menu", &showInputMethodMenu, NULL);
+ return showInputMethodMenu;
+ }
+
+ void checkActionState(GtkAction* action, unsigned state)
+ {
+ if (state & Visible)
+ g_assert(gtk_action_get_visible(action));
+ else
+ g_assert(!gtk_action_get_visible(action));
+
+ if (state & Enabled)
+ g_assert(gtk_action_get_sensitive(action));
+ else
+ g_assert(!gtk_action_get_sensitive(action));
+
+ if (GTK_IS_TOGGLE_ACTION(action)) {
+ if (state & Checked)
+ g_assert(gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)));
+ else
+ g_assert(!gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)));
+ }
+ }
+
+ GList* checkCurrentItemIsStockActionAndGetNext(GList* items, WebKitContextMenuAction stockAction, unsigned state)
+ {
+ g_assert(items);
+ g_assert(WEBKIT_IS_CONTEXT_MENU_ITEM(items->data));
+
+ WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(items->data);
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(item));
+
+ GtkAction* action = webkit_context_menu_item_get_action(item);
+ g_assert(GTK_IS_ACTION(action));
+
+ g_assert_cmpint(webkit_context_menu_item_get_stock_action(item), ==, stockAction);
+
+ checkActionState(action, state);
+
+ return g_list_next(items);
+ }
+
+ GList* checkCurrentItemIsCustomActionAndGetNext(GList* items, const char* label, unsigned state)
+ {
+ g_assert(items);
+ g_assert(WEBKIT_IS_CONTEXT_MENU_ITEM(items->data));
+
+ WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(items->data);
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(item));
+
+ GtkAction* action = webkit_context_menu_item_get_action(item);
+ g_assert(GTK_IS_ACTION(action));
+
+ g_assert_cmpint(webkit_context_menu_item_get_stock_action(item), ==, WEBKIT_CONTEXT_MENU_ACTION_CUSTOM);
+ g_assert_cmpstr(gtk_action_get_label(action), ==, label);
+
+ checkActionState(action, state);
+
+ return g_list_next(items);
+ }
+
+ GList* checkCurrentItemIsSubMenuAndGetNext(GList* items, const char* label, unsigned state, GList** subMenuIter)
+ {
+ g_assert(items);
+ g_assert(WEBKIT_IS_CONTEXT_MENU_ITEM(items->data));
+
+ WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(items->data);
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(item));
+
+ GtkAction* action = webkit_context_menu_item_get_action(item);
+ g_assert(GTK_IS_ACTION(action));
+
+ g_assert_cmpstr(gtk_action_get_label(action), ==, label);
+ checkActionState(action, state);
+
+ WebKitContextMenu* subMenu = webkit_context_menu_item_get_submenu(item);
+ g_assert(WEBKIT_IS_CONTEXT_MENU(subMenu));
+ if (subMenuIter)
+ *subMenuIter = webkit_context_menu_get_items(subMenu);
+
+ return g_list_next(items);
+ }
+
+ GList* checkCurrentItemIsSeparatorAndGetNext(GList* items)
+ {
+ g_assert(items);
+ g_assert(WEBKIT_IS_CONTEXT_MENU_ITEM(items->data));
+
+ WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(items->data);
+ g_assert(webkit_context_menu_item_is_separator(item));
+
+ return g_list_next(items);
+ }
+
+ static gboolean doRightClickIdleCallback(ContextMenuTest* test)
+ {
+ test->clickMouseButton(test->m_menuPositionX, test->m_menuPositionY, 3);
+ return FALSE;
+ }
+
+ void showContextMenuAtPositionAndWaitUntilFinished(int x, int y)
+ {
+ m_menuPositionX = x;
+ m_menuPositionY = y;
+ g_idle_add(reinterpret_cast<GSourceFunc>(doRightClickIdleCallback), this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ void showContextMenuAndWaitUntilFinished()
+ {
+ showContextMenuAtPositionAndWaitUntilFinished(0, 0);
+ }
+
+ static gboolean simulateEscKeyIdleCallback(ContextMenuTest* test)
+ {
+ test->keyStroke(GDK_KEY_Escape);
+ return FALSE;
+ }
+
+ void dismissContextMenuAndWaitUntilFinished()
+ {
+ g_idle_add(reinterpret_cast<GSourceFunc>(simulateEscKeyIdleCallback), this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ double m_menuPositionX;
+ double m_menuPositionY;
+};
+
+class ContextMenuDefaultTest: public ContextMenuTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ContextMenuDefaultTest);
+
+ enum DefaultMenuType {
+ Navigation,
+ Link,
+ Image,
+ LinkImage,
+ Video,
+ Audio,
+ Editable,
+ Selection
+ };
+
+ ContextMenuDefaultTest()
+ : m_expectedMenuType(Navigation)
+ {
+ }
+
+ bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent* event, WebKitHitTestResult* hitTestResult)
+ {
+ GList* iter = webkit_context_menu_get_items(contextMenu);
+
+ switch (m_expectedMenuType) {
+ case Navigation:
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_GO_BACK, Visible);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD, Visible);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_STOP, Visible);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_RELOAD, Visible | Enabled);
+ break;
+ case Link:
+ g_assert(webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK_IN_NEW_WINDOW, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_LINK_TO_CLIPBOARD, Visible | Enabled);
+ break;
+ case Image:
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_IMAGE_IN_NEW_WINDOW, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_IMAGE_TO_DISK, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_IMAGE_TO_CLIPBOARD, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_IMAGE_URL_TO_CLIPBOARD, Visible | Enabled);
+ break;
+ case LinkImage:
+ g_assert(webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK_IN_NEW_WINDOW, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_LINK_TO_CLIPBOARD, Visible | Enabled);
+ iter = checkCurrentItemIsSeparatorAndGetNext(iter);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_IMAGE_IN_NEW_WINDOW, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_IMAGE_TO_DISK, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_IMAGE_TO_CLIPBOARD, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_IMAGE_URL_TO_CLIPBOARD, Visible | Enabled);
+ break;
+ case Video:
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_PLAY, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_MUTE, Visible);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_TOGGLE_MEDIA_CONTROLS, Visible | Enabled | Checked);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_TOGGLE_MEDIA_LOOP, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_ENTER_VIDEO_FULLSCREEN, Visible | Enabled);
+ iter = checkCurrentItemIsSeparatorAndGetNext(iter);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_VIDEO_LINK_TO_CLIPBOARD, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_VIDEO_IN_NEW_WINDOW, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_VIDEO_TO_DISK, Visible | Enabled);
+ break;
+ case Audio:
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_PLAY, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_MUTE, Visible);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_TOGGLE_MEDIA_CONTROLS, Visible | Enabled | Checked);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_TOGGLE_MEDIA_LOOP, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_ENTER_VIDEO_FULLSCREEN, Visible);
+ iter = checkCurrentItemIsSeparatorAndGetNext(iter);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_AUDIO_LINK_TO_CLIPBOARD, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_AUDIO_IN_NEW_WINDOW, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_AUDIO_TO_DISK, Visible | Enabled);
+ break;
+ case Editable:
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_CUT, Visible);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY, Visible);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_PASTE, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DELETE, Visible);
+ iter = checkCurrentItemIsSeparatorAndGetNext(iter);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_SELECT_ALL, Visible | Enabled);
+ iter = checkCurrentItemIsSeparatorAndGetNext(iter);
+ if (shouldShowInputMethodsMenu())
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_INPUT_METHODS, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_UNICODE, Visible | Enabled);
+ break;
+ case Selection:
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_selection(hitTestResult));
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY, Visible | Enabled);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (webkit_settings_get_enable_developer_extras(webkit_web_view_get_settings(m_webView))) {
+ iter = checkCurrentItemIsSeparatorAndGetNext(iter);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_INSPECT_ELEMENT, Visible | Enabled);
+ }
+ g_assert(!iter);
+
+ quitMainLoop();
+
+ return true;
+ }
+
+ DefaultMenuType m_expectedMenuType;
+};
+
+static void testContextMenuDefaultMenu(ContextMenuDefaultTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+
+ const char* linksHTML =
+ "<html><head>"
+ " <script>"
+ " window.onload = function () {"
+ " window.getSelection().removeAllRanges();"
+ " var select_range = document.createRange();"
+ " select_range.selectNodeContents(document.getElementById('text_to_select'));"
+ " window.getSelection().addRange(select_range);"
+ " }"
+ " </script>"
+ "</head><body>"
+ " <a style='position:absolute; left:1; top:1' href='http://www.webkitgtk.org' title='WebKitGTK+ Title'>WebKitGTK+ Website</a>"
+ " <img style='position:absolute; left:1; top:10' src='0xdeadbeef' width=5 height=5></img>"
+ " <a style='position:absolute; left:1; top:20' href='http://www.webkitgtk.org/logo' title='WebKitGTK+ Logo'><img src='0xdeadbeef' width=5 height=5></img></a>"
+ " <input style='position:absolute; left:1; top:30' size='10'></input>"
+ " <video style='position:absolute; left:1; top:50' width='300' height='300' controls='controls' preload='none'><source src='movie.ogg' type='video/ogg' /></video>"
+ " <audio style='position:absolute; left:1; top:60' width='50' height='20' controls='controls' preload='none'><source src='track.mp3' type='audio/mp3' /></audio>"
+ " <p style='position:absolute; left:1; top:90' id='text_to_select'>Lorem ipsum.</p>"
+ "</body></html>";
+ test->loadHtml(linksHTML, "file:///");
+ test->waitUntilLoadFinished();
+
+ // Context menu for selection.
+ // This test should always be the first because any other click removes the selection.
+ test->m_expectedMenuType = ContextMenuDefaultTest::Selection;
+ test->showContextMenuAtPositionAndWaitUntilFinished(2, 115);
+
+ // Context menu for document.
+ test->m_expectedMenuType = ContextMenuDefaultTest::Navigation;
+ test->showContextMenuAtPositionAndWaitUntilFinished(0, 0);
+
+ // Context menu for link.
+ test->m_expectedMenuType = ContextMenuDefaultTest::Link;
+ test->showContextMenuAtPositionAndWaitUntilFinished(1, 1);
+
+ // Context menu for image.
+ test->m_expectedMenuType = ContextMenuDefaultTest::Image;
+ test->showContextMenuAtPositionAndWaitUntilFinished(1, 10);
+
+ // Enable developer extras now, so that inspector element
+ // will be shown in the default context menu.
+ webkit_settings_set_enable_developer_extras(webkit_web_view_get_settings(test->m_webView), TRUE);
+
+ // Context menu for image link.
+ test->m_expectedMenuType = ContextMenuDefaultTest::LinkImage;
+ test->showContextMenuAtPositionAndWaitUntilFinished(1, 20);
+
+ // Context menu for video.
+ test->m_expectedMenuType = ContextMenuDefaultTest::Video;
+ test->showContextMenuAtPositionAndWaitUntilFinished(1, 50);
+
+ // Context menu for audio.
+ test->m_expectedMenuType = ContextMenuDefaultTest::Audio;
+ test->showContextMenuAtPositionAndWaitUntilFinished(1, 60);
+
+ // Context menu for editable.
+ test->m_expectedMenuType = ContextMenuDefaultTest::Editable;
+ test->showContextMenuAtPositionAndWaitUntilFinished(5, 35);
+}
+
+class ContextMenuCustomTest: public ContextMenuTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ContextMenuCustomTest);
+
+ ContextMenuCustomTest()
+ : m_itemToActivateLabel(0)
+ , m_activated(false)
+ , m_toggled(false)
+ {
+ }
+
+ bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult* hitTestResult)
+ {
+ // Append our custom item to the default menu.
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new(m_action.get()));
+ quitMainLoop();
+
+ return false;
+ }
+
+ GtkMenuItem* getMenuItem(GtkMenu* menu, const gchar* itemLabel)
+ {
+ GUniquePtr<GList> items(gtk_container_get_children(GTK_CONTAINER(menu)));
+ for (GList* iter = items.get(); iter; iter = g_list_next(iter)) {
+ GtkMenuItem* child = GTK_MENU_ITEM(iter->data);
+ if (g_str_equal(itemLabel, gtk_menu_item_get_label(child)))
+ return child;
+ }
+ g_assert_not_reached();
+ return 0;
+ }
+
+ void activateMenuItem()
+ {
+ g_assert(m_itemToActivateLabel);
+ GtkMenu* menu = getPopupMenu();
+ GtkMenuItem* item = getMenuItem(menu, m_itemToActivateLabel);
+ gtk_menu_shell_activate_item(GTK_MENU_SHELL(menu), GTK_WIDGET(item), TRUE);
+ m_itemToActivateLabel = 0;
+ }
+
+ static gboolean activateMenuItemIdleCallback(gpointer userData)
+ {
+ ContextMenuCustomTest* test = static_cast<ContextMenuCustomTest*>(userData);
+ test->activateMenuItem();
+ return FALSE;
+ }
+
+ void activateCustomMenuItemAndWaitUntilActivated(const char* actionLabel)
+ {
+ m_activated = m_toggled = false;
+ m_itemToActivateLabel = actionLabel;
+ g_idle_add(activateMenuItemIdleCallback, this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ void toggleCustomMenuItemAndWaitUntilToggled(const char* actionLabel)
+ {
+ activateCustomMenuItemAndWaitUntilActivated(actionLabel);
+ }
+
+ static void actionActivatedCallback(GtkAction*, ContextMenuCustomTest* test)
+ {
+ test->m_activated = true;
+ }
+
+ static void actionToggledCallback(GtkAction*, ContextMenuCustomTest* test)
+ {
+ test->m_toggled = true;
+ }
+
+ void setAction(GtkAction* action)
+ {
+ m_action = action;
+ if (GTK_IS_TOGGLE_ACTION(action))
+ g_signal_connect(action, "toggled", G_CALLBACK(actionToggledCallback), this);
+ else
+ g_signal_connect(action, "activate", G_CALLBACK(actionActivatedCallback), this);
+ }
+
+ GRefPtr<GtkAction> m_action;
+ const char* m_itemToActivateLabel;
+ bool m_activated;
+ bool m_toggled;
+};
+
+static void testContextMenuPopulateMenu(ContextMenuCustomTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+
+ test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
+ test->waitUntilLoadFinished();
+
+ // Create a custom menu item.
+ GRefPtr<GtkAction> action = adoptGRef(gtk_action_new("WebKitGTK+CustomAction", "Custom _Action", 0, 0));
+ test->setAction(action.get());
+ test->showContextMenuAndWaitUntilFinished();
+ test->activateCustomMenuItemAndWaitUntilActivated(gtk_action_get_label(action.get()));
+ g_assert(test->m_activated);
+ g_assert(!test->m_toggled);
+
+ // Create a custom toggle menu item.
+ GRefPtr<GtkAction> toggleAction = adoptGRef(GTK_ACTION(gtk_toggle_action_new("WebKitGTK+CustomToggleAction", "Custom _Toggle Action", 0, 0)));
+ test->setAction(toggleAction.get());
+ test->showContextMenuAndWaitUntilFinished();
+ test->toggleCustomMenuItemAndWaitUntilToggled(gtk_action_get_label(toggleAction.get()));
+ g_assert(!test->m_activated);
+ g_assert(test->m_toggled);
+}
+
+class ContextMenuCustomFullTest: public ContextMenuTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ContextMenuCustomFullTest);
+
+ bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*)
+ {
+ // Clear proposed menu and build our own.
+ webkit_context_menu_remove_all(contextMenu);
+ g_assert_cmpint(webkit_context_menu_get_n_items(contextMenu), ==, 0);
+
+ // Add actions from stock.
+ webkit_context_menu_prepend(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_GO_BACK));
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD));
+ webkit_context_menu_insert(contextMenu, webkit_context_menu_item_new_separator(), 2);
+
+ // Add custom actions.
+ GRefPtr<GtkAction> action = adoptGRef(gtk_action_new("WebKitGTK+CustomAction", "Custom _Action", 0, 0));
+ gtk_action_set_sensitive(action.get(), FALSE);
+ webkit_context_menu_insert(contextMenu, webkit_context_menu_item_new(action.get()), -1);
+ GRefPtr<GtkAction> toggleAction = adoptGRef(GTK_ACTION(gtk_toggle_action_new("WebKitGTK+CustomToggleAction", "Custom _Toggle Action", 0, 0)));
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggleAction.get()), TRUE);
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new(toggleAction.get()));
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
+
+ // Add a submenu.
+ GRefPtr<WebKitContextMenu> subMenu = adoptGRef(webkit_context_menu_new());
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(subMenu.get()));
+ webkit_context_menu_insert(subMenu.get(), webkit_context_menu_item_new_from_stock_action_with_label(WEBKIT_CONTEXT_MENU_ACTION_STOP, "Stop Load"), 0);
+ webkit_context_menu_insert(subMenu.get(), webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_RELOAD), -1);
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_with_submenu("Load options", subMenu.get()));
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
+
+ // Move Load submenu before custom actions.
+ webkit_context_menu_move_item(contextMenu, webkit_context_menu_last(contextMenu), 3);
+ webkit_context_menu_move_item(contextMenu, webkit_context_menu_last(contextMenu), 3);
+
+ // If last item is a separator, remove it.
+ if (webkit_context_menu_item_is_separator(webkit_context_menu_last(contextMenu)))
+ webkit_context_menu_remove(contextMenu, webkit_context_menu_last(contextMenu));
+
+ // Check the menu.
+ GList* iter = webkit_context_menu_get_items(contextMenu);
+
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_GO_BACK, Visible | Enabled);
+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD, Visible | Enabled);
+ iter = checkCurrentItemIsSeparatorAndGetNext(iter);
+
+ GList* subMenuIter = 0;
+ iter = checkCurrentItemIsSubMenuAndGetNext(iter, "Load options", Visible | Enabled, &subMenuIter);
+ subMenuIter = checkCurrentItemIsStockActionAndGetNext(subMenuIter, WEBKIT_CONTEXT_MENU_ACTION_STOP, Visible | Enabled);
+ subMenuIter = checkCurrentItemIsStockActionAndGetNext(subMenuIter, WEBKIT_CONTEXT_MENU_ACTION_RELOAD, Visible | Enabled);
+ iter = checkCurrentItemIsSeparatorAndGetNext(iter);
+
+ iter = checkCurrentItemIsCustomActionAndGetNext(iter, "Custom _Action", Visible);
+ iter = checkCurrentItemIsCustomActionAndGetNext(iter, "Custom _Toggle Action", Visible | Enabled | Checked);
+ g_assert(!iter);
+
+ quitMainLoop();
+
+ return true;
+ }
+};
+
+static void testContextMenuCustomMenu(ContextMenuCustomFullTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+
+ test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
+ test->waitUntilLoadFinished();
+
+ test->showContextMenuAndWaitUntilFinished();
+}
+
+class ContextMenuDisabledTest: public ContextMenuTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ContextMenuDisabledTest);
+
+ enum DisableMode {
+ IgnoreClicks,
+ IgnoreDefaultMenu
+ };
+
+ static gboolean buttonPressEventCallback(GtkWidget*, GdkEvent* event, ContextMenuDisabledTest* test)
+ {
+ if (event->button.button != 3)
+ return FALSE;
+ return test->rightButtonPressed();
+ }
+
+ ContextMenuDisabledTest()
+ : m_disableMode(IgnoreClicks)
+ {
+ g_signal_connect(m_webView, "button-press-event", G_CALLBACK(buttonPressEventCallback), this);
+ }
+
+ bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*)
+ {
+ if (m_disableMode == IgnoreClicks)
+ g_assert_not_reached();
+ else
+ quitMainLoop();
+
+ return true;
+ }
+
+ bool rightButtonPressed()
+ {
+ if (m_disableMode == IgnoreClicks) {
+ quitMainLoopAfterProcessingPendingEvents();
+ return true;
+ }
+ return false;
+ }
+
+ DisableMode m_disableMode;
+};
+
+static void testContextMenuDisableMenu(ContextMenuDisabledTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+
+ test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
+ test->waitUntilLoadFinished();
+
+ test->m_disableMode = ContextMenuDisabledTest::IgnoreDefaultMenu;
+ test->showContextMenuAndWaitUntilFinished();
+
+ test->m_disableMode = ContextMenuDisabledTest::IgnoreClicks;
+ test->showContextMenuAndWaitUntilFinished();
+}
+
+class ContextMenuSubmenuTest: public ContextMenuTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ContextMenuSubmenuTest);
+
+ bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*)
+ {
+ size_t menuSize = webkit_context_menu_get_n_items(contextMenu);
+ GRefPtr<WebKitContextMenu> subMenu = adoptGRef(webkit_context_menu_new());
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_with_submenu("SubMenuItem", subMenu.get()));
+ g_assert_cmpuint(webkit_context_menu_get_n_items(contextMenu), ==, menuSize + 1);
+
+ GRefPtr<WebKitContextMenu> subMenu2 = adoptGRef(webkit_context_menu_new());
+ GRefPtr<WebKitContextMenuItem> item = webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK);
+
+ // Add submenu to newly created item.
+ g_assert(!webkit_context_menu_item_get_submenu(item.get()));
+ webkit_context_menu_item_set_submenu(item.get(), subMenu2.get());
+ g_assert(webkit_context_menu_item_get_submenu(item.get()) == subMenu2.get());
+
+ // Replace the submenu.
+ webkit_context_menu_item_set_submenu(item.get(), 0);
+ g_assert(!webkit_context_menu_item_get_submenu(item.get()));
+
+ // Try to add a submenu already added to another item.
+ removeLogFatalFlag(G_LOG_LEVEL_WARNING);
+ webkit_context_menu_item_set_submenu(item.get(), subMenu.get());
+ addLogFatalFlag(G_LOG_LEVEL_WARNING);
+ g_assert(!webkit_context_menu_item_get_submenu(item.get()));
+
+ // A removed submenu shouldn't have a parent.
+ webkit_context_menu_item_set_submenu(item.get(), subMenu2.get());
+ g_assert(webkit_context_menu_item_get_submenu(item.get()) == subMenu2.get());
+
+ quitMainLoop();
+
+ return true;
+ }
+};
+
+static void testContextMenuSubMenu(ContextMenuSubmenuTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+
+ test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
+ test->waitUntilLoadFinished();
+
+ test->showContextMenuAndWaitUntilFinished();
+}
+
+class ContextMenuDismissedTest: public ContextMenuTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ContextMenuDismissedTest);
+
+ ContextMenuDismissedTest()
+ : m_dismissed(false)
+ {
+ }
+
+ bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*)
+ {
+ quitMainLoop();
+ // Show the default context menu.
+ return false;
+ }
+
+ void contextMenuDismissed()
+ {
+ m_dismissed = true;
+ ContextMenuTest::contextMenuDismissed();
+ }
+
+ void showContextMenuAndWaitUntilDismissed()
+ {
+ showContextMenuAndWaitUntilFinished();
+ dismissContextMenuAndWaitUntilFinished();
+ }
+
+ bool m_dismissed;
+};
+
+static void testContextMenuDismissed(ContextMenuDismissedTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+
+ test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
+ test->waitUntilLoadFinished();
+
+ test->showContextMenuAndWaitUntilDismissed();
+ g_assert(test->m_dismissed);
+}
+
+class ContextMenuSmartSeparatorsTest: public ContextMenuTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ContextMenuSmartSeparatorsTest);
+
+ bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*)
+ {
+ webkit_context_menu_remove_all(contextMenu);
+
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_GO_BACK));
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD));
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_COPY));
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_INSPECT_ELEMENT));
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
+ webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
+
+ quitMainLoop();
+
+ return false;
+ }
+
+ GtkMenu* showContextMenuAndGetGtkMenu()
+ {
+ showContextMenuAndWaitUntilFinished();
+ return getPopupMenu();
+ }
+};
+
+static void testContextMenuSmartSeparators(ContextMenuSmartSeparatorsTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+
+ test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
+ test->waitUntilLoadFinished();
+
+ GtkMenu* menu = test->showContextMenuAndGetGtkMenu();
+ g_assert(menu);
+
+ // Leading and trailing separators are not added to the context menu.
+ GUniquePtr<GList> menuItems(gtk_container_get_children(GTK_CONTAINER(menu)));
+ g_assert_cmpuint(g_list_length(menuItems.get()), ==, 6);
+ GtkWidget* item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2));
+ g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4));
+ g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+
+ // Hiding a menu item between two separators hides the following separator.
+ GtkAction* action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(g_list_nth_data(menuItems.get(), 3)));
+ gtk_action_set_visible(action, FALSE);
+ menuItems.reset(gtk_container_get_children(GTK_CONTAINER(menu)));
+ g_assert_cmpuint(g_list_length(menuItems.get()), ==, 6);
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2));
+ g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4));
+ g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ gtk_action_set_visible(action, TRUE);
+
+ // Showing an action between two separators shows the hidden separator.
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2));
+ g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4));
+ g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+
+ // Trailing separators are hidden too.
+ action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(g_list_nth_data(menuItems.get(), 5)));
+ gtk_action_set_visible(action, FALSE);
+ menuItems.reset(gtk_container_get_children(GTK_CONTAINER(menu)));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2));
+ g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4));
+ g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item));
+ item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5));
+ g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item));
+}
+
+class ContextMenuWebExtensionTest: public ContextMenuTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ContextMenuWebExtensionTest);
+
+ void deserializeContextMenuFromUserData(GVariant* userData)
+ {
+ m_actions.clear();
+ if (!userData)
+ return;
+
+ GVariantIter iter;
+ g_variant_iter_init(&iter, userData);
+ m_actions.reserveInitialCapacity(g_variant_iter_n_children(&iter));
+
+ uint32_t item;
+ while (g_variant_iter_next(&iter, "u", &item))
+ m_actions.uncheckedAppend(static_cast<WebKitContextMenuAction>(item));
+ }
+
+ bool contextMenu(WebKitContextMenu* menu, GdkEvent*, WebKitHitTestResult*)
+ {
+ deserializeContextMenuFromUserData(webkit_context_menu_get_user_data(menu));
+ GList* items = webkit_context_menu_get_items(menu);
+ g_assert_cmpuint(g_list_length(items), ==, m_actions.size());
+
+ unsigned actionIndex = 0;
+ for (GList* it = items; it; it = g_list_next(it)) {
+ WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(it->data);
+ g_assert_cmpuint(webkit_context_menu_item_get_stock_action(item), ==, m_actions[actionIndex++]);
+ }
+
+ quitMainLoop();
+
+ return true;
+ }
+
+ Vector<WebKitContextMenuAction> m_actions;
+};
+
+static void testContextMenuWebExtensionMenu(ContextMenuWebExtensionTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+ test->loadHtml("<html><body>WebKitGTK+ Context menu tests<br>"
+ "<a style='position:absolute; left:1; top:10' href='http://www.webkitgtk.org'>WebKitGTK+ Website</a></body></html>",
+ "ContextMenuTestDefault");
+ test->waitUntilLoadFinished();
+
+ // Default context menu.
+ test->showContextMenuAtPositionAndWaitUntilFinished(1, 1);
+ g_assert_cmpuint(test->m_actions.size(), ==, 4);
+ g_assert_cmpuint(test->m_actions[0], ==, WEBKIT_CONTEXT_MENU_ACTION_GO_BACK);
+ g_assert_cmpuint(test->m_actions[1], ==, WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD);
+ g_assert_cmpuint(test->m_actions[2], ==, WEBKIT_CONTEXT_MENU_ACTION_STOP);
+ g_assert_cmpuint(test->m_actions[3], ==, WEBKIT_CONTEXT_MENU_ACTION_RELOAD);
+
+ // Link menu.
+ test->showContextMenuAtPositionAndWaitUntilFinished(1, 11);
+ g_assert_cmpuint(test->m_actions.size(), ==, 4);
+ g_assert_cmpuint(test->m_actions[0], ==, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK);
+ g_assert_cmpuint(test->m_actions[1], ==, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK_IN_NEW_WINDOW);
+ g_assert_cmpuint(test->m_actions[2], ==, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK);
+ g_assert_cmpuint(test->m_actions[3], ==, WEBKIT_CONTEXT_MENU_ACTION_COPY_LINK_TO_CLIPBOARD);
+
+ // Custom menu.
+ test->loadHtml("<html><body></body></html>", "ContextMenuTestCustom");
+ test->showContextMenuAndWaitUntilFinished();
+ g_assert_cmpuint(test->m_actions.size(), ==, 4);
+ g_assert_cmpuint(test->m_actions[0], ==, WEBKIT_CONTEXT_MENU_ACTION_STOP);
+ g_assert_cmpuint(test->m_actions[1], ==, WEBKIT_CONTEXT_MENU_ACTION_RELOAD);
+ g_assert_cmpuint(test->m_actions[2], ==, WEBKIT_CONTEXT_MENU_ACTION_NO_ACTION);
+ g_assert_cmpuint(test->m_actions[3], ==, WEBKIT_CONTEXT_MENU_ACTION_INSPECT_ELEMENT);
+
+ // Menu cleared by the web process.
+ test->loadHtml("<html><body></body></html>", "ContextMenuTestClear");
+ test->showContextMenuAndWaitUntilFinished();
+ g_assert_cmpuint(test->m_actions.size(), ==, 0);
+}
+
+class ContextMenuWebExtensionNodeTest: public ContextMenuTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ContextMenuWebExtensionNodeTest);
+
+ struct Node {
+ enum {
+ NodeUnknown = 0,
+ NodeElement = 1,
+ NodeText = 3
+ };
+ typedef unsigned Type;
+
+ CString name;
+ Type type;
+ CString contents;
+ CString parentName;
+ };
+
+ void deserializeNodeFromUserData(GVariant* userData)
+ {
+ GVariantIter iter;
+ g_variant_iter_init(&iter, userData);
+
+ const char* key;
+ GVariant* value;
+ while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) {
+ if (!strcmp(key, "Name") && g_variant_classify(value) == G_VARIANT_CLASS_STRING)
+ m_node.name = g_variant_get_string(value, nullptr);
+ else if (!strcmp(key, "Type") && g_variant_classify(value) == G_VARIANT_CLASS_UINT32)
+ m_node.type = g_variant_get_uint32(value);
+ else if (!strcmp(key, "Contents") && g_variant_classify(value) == G_VARIANT_CLASS_STRING)
+ m_node.contents = g_variant_get_string(value, nullptr);
+ else if (!strcmp(key, "Parent") && g_variant_classify(value) == G_VARIANT_CLASS_STRING)
+ m_node.parentName = g_variant_get_string(value, nullptr);
+ g_variant_unref(value);
+ }
+ }
+
+ bool contextMenu(WebKitContextMenu* menu, GdkEvent*, WebKitHitTestResult*)
+ {
+ deserializeNodeFromUserData(webkit_context_menu_get_user_data(menu));
+ quitMainLoop();
+
+ return true;
+ }
+
+ Node m_node;
+};
+
+static void testContextMenuWebExtensionNode(ContextMenuWebExtensionNodeTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+ test->loadHtml("<html><body><p style='position:absolute; left:1; top:1'>WebKitGTK+ Context menu tests</p><br>"
+ "<a style='position:absolute; left:1; top:100' href='http://www.webkitgtk.org'>WebKitGTK+ Website</a></body></html>",
+ "ContextMenuTestNode");
+ test->waitUntilLoadFinished();
+
+ test->showContextMenuAtPositionAndWaitUntilFinished(0, 0);
+ g_assert_cmpstr(test->m_node.name.data(), ==, "HTML");
+ g_assert_cmpuint(test->m_node.type, ==, ContextMenuWebExtensionNodeTest::Node::NodeElement);
+ g_assert_cmpstr(test->m_node.contents.data(), ==, "WebKitGTK+ Context menu testsWebKitGTK+ Website");
+ g_assert_cmpstr(test->m_node.parentName.data(), ==, "#document");
+
+ test->showContextMenuAtPositionAndWaitUntilFinished(1, 20);
+ g_assert_cmpstr(test->m_node.name.data(), ==, "#text");
+ g_assert_cmpuint(test->m_node.type, ==, ContextMenuWebExtensionNodeTest::Node::NodeText);
+ g_assert_cmpstr(test->m_node.contents.data(), ==, "WebKitGTK+ Context menu tests");
+ g_assert_cmpstr(test->m_node.parentName.data(), ==, "P");
+
+ // Link menu.
+ test->showContextMenuAtPositionAndWaitUntilFinished(1, 101);
+ g_assert_cmpstr(test->m_node.name.data(), ==, "#text");
+ g_assert_cmpuint(test->m_node.type, ==, ContextMenuWebExtensionNodeTest::Node::NodeText);
+ g_assert_cmpstr(test->m_node.contents.data(), ==, "WebKitGTK+ Website");
+ g_assert_cmpstr(test->m_node.parentName.data(), ==, "A");
+}
+
+void beforeAll()
+{
+ ContextMenuDefaultTest::add("WebKitWebView", "default-menu", testContextMenuDefaultMenu);
+ ContextMenuCustomTest::add("WebKitWebView", "populate-menu", testContextMenuPopulateMenu);
+ ContextMenuCustomFullTest::add("WebKitWebView", "custom-menu", testContextMenuCustomMenu);
+ ContextMenuDisabledTest::add("WebKitWebView", "disable-menu", testContextMenuDisableMenu);
+ ContextMenuSubmenuTest::add("WebKitWebView", "submenu", testContextMenuSubMenu);
+ ContextMenuDismissedTest::add("WebKitWebView", "menu-dismissed", testContextMenuDismissed);
+ ContextMenuSmartSeparatorsTest::add("WebKitWebView", "smart-separators", testContextMenuSmartSeparators);
+ ContextMenuWebExtensionTest::add("WebKitWebPage", "context-menu", testContextMenuWebExtensionMenu);
+ ContextMenuWebExtensionNodeTest::add("WebKitWebPage", "context-menu-node", testContextMenuWebExtensionNode);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestCookieManager.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestCookieManager.cpp
new file mode 100644
index 000000000..1e842cd04
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestCookieManager.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebKitTestServer.h"
+#include "WebViewTest.h"
+#include <glib/gstdio.h>
+
+static WebKitTestServer* kServer;
+
+static const char* kFirstPartyDomain = "127.0.0.1";
+static const char* kThirdPartyDomain = "localhost";
+static const char* kIndexHtmlFormat =
+ "<html><body>"
+ " <p>WebKitGTK+ Cookie Manager test</p>"
+ " <img src='http://localhost:%u/image.png' width=5 height=5></img>"
+ "</body></html>";
+
+class CookieManagerTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(CookieManagerTest);
+
+ static void cookiesChangedCallback(WebKitCookieManager*, CookieManagerTest* test)
+ {
+ test->m_cookiesChanged = true;
+ if (test->m_finishLoopWhenCookiesChange)
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ CookieManagerTest()
+ : WebViewTest()
+ , m_cookieManager(webkit_web_context_get_cookie_manager(webkit_web_view_get_context(m_webView)))
+ , m_acceptPolicy(WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY)
+ , m_domains(0)
+ , m_cookiesChanged(false)
+ , m_finishLoopWhenCookiesChange(false)
+ {
+ g_signal_connect(m_cookieManager, "changed", G_CALLBACK(cookiesChangedCallback), this);
+ }
+
+ ~CookieManagerTest()
+ {
+ g_strfreev(m_domains);
+ g_signal_handlers_disconnect_matched(m_cookieManager, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ if (m_cookiesTextFile)
+ g_unlink(m_cookiesTextFile.get());
+ if (m_cookiesSQLiteFile)
+ g_unlink(m_cookiesSQLiteFile.get());
+ }
+
+ void setPersistentStorage(WebKitCookiePersistentStorage storage)
+ {
+ const char* filename = 0;
+ switch (storage) {
+ case WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT:
+ if (!m_cookiesTextFile)
+ m_cookiesTextFile.reset(g_build_filename(Test::dataDirectory(), "cookies.txt", nullptr));
+ filename = m_cookiesTextFile.get();
+ break;
+ case WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE:
+ if (!m_cookiesSQLiteFile)
+ m_cookiesSQLiteFile.reset(g_build_filename(Test::dataDirectory(), "cookies.db", nullptr));
+ filename = m_cookiesSQLiteFile.get();
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ webkit_cookie_manager_set_persistent_storage(m_cookieManager, filename, storage);
+ }
+
+ static void getAcceptPolicyReadyCallback(GObject* object, GAsyncResult* result, gpointer userData)
+ {
+ GUniqueOutPtr<GError> error;
+ WebKitCookieAcceptPolicy policy = webkit_cookie_manager_get_accept_policy_finish(WEBKIT_COOKIE_MANAGER(object), result, &error.outPtr());
+ g_assert(!error.get());
+
+ CookieManagerTest* test = static_cast<CookieManagerTest*>(userData);
+ test->m_acceptPolicy = policy;
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ WebKitCookieAcceptPolicy getAcceptPolicy()
+ {
+ m_acceptPolicy = WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY;
+ webkit_cookie_manager_get_accept_policy(m_cookieManager, 0, getAcceptPolicyReadyCallback, this);
+ g_main_loop_run(m_mainLoop);
+
+ return m_acceptPolicy;
+ }
+
+ void setAcceptPolicy(WebKitCookieAcceptPolicy policy)
+ {
+ webkit_cookie_manager_set_accept_policy(m_cookieManager, policy);
+ }
+
+ static void getDomainsReadyCallback(GObject* object, GAsyncResult* result, gpointer userData)
+ {
+ GUniqueOutPtr<GError> error;
+ char** domains = webkit_cookie_manager_get_domains_with_cookies_finish(WEBKIT_COOKIE_MANAGER(object), result, &error.outPtr());
+ g_assert(!error.get());
+
+ CookieManagerTest* test = static_cast<CookieManagerTest*>(userData);
+ test->m_domains = domains;
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ char** getDomains()
+ {
+ g_strfreev(m_domains);
+ m_domains = 0;
+ webkit_cookie_manager_get_domains_with_cookies(m_cookieManager, 0, getDomainsReadyCallback, this);
+ g_main_loop_run(m_mainLoop);
+
+ return m_domains;
+ }
+
+ bool hasDomain(const char* domain)
+ {
+ if (!m_domains)
+ return false;
+
+ for (size_t i = 0; m_domains[i]; ++i)
+ if (g_str_equal(m_domains[i], domain))
+ return true;
+ return false;
+ }
+
+ void deleteCookiesForDomain(const char* domain)
+ {
+ webkit_cookie_manager_delete_cookies_for_domain(m_cookieManager, domain);
+ }
+
+ void deleteAllCookies()
+ {
+ webkit_cookie_manager_delete_all_cookies(m_cookieManager);
+ }
+
+ void waitUntilCookiesChanged()
+ {
+ m_cookiesChanged = false;
+ m_finishLoopWhenCookiesChange = true;
+ g_main_loop_run(m_mainLoop);
+ m_finishLoopWhenCookiesChange = false;
+ }
+
+ WebKitCookieManager* m_cookieManager;
+ WebKitCookieAcceptPolicy m_acceptPolicy;
+ char** m_domains;
+ bool m_cookiesChanged;
+ bool m_finishLoopWhenCookiesChange;
+ GUniquePtr<char> m_cookiesTextFile;
+ GUniquePtr<char> m_cookiesSQLiteFile;
+};
+
+static void testCookieManagerAcceptPolicy(CookieManagerTest* test, gconstpointer)
+{
+ // Default policy is NO_THIRD_PARTY.
+ g_assert_cmpint(test->getAcceptPolicy(), ==, WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY);
+ test->loadURI(kServer->getURIForPath("/index.html").data());
+ test->waitUntilLoadFinished();
+ char** domains = test->getDomains();
+ g_assert(domains);
+ g_assert_cmpint(g_strv_length(domains), ==, 1);
+ g_assert_cmpstr(domains[0], ==, kFirstPartyDomain);
+ test->deleteAllCookies();
+
+ test->setAcceptPolicy(WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS);
+ g_assert_cmpint(test->getAcceptPolicy(), ==, WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS);
+ test->loadURI(kServer->getURIForPath("/index.html").data());
+ test->waitUntilLoadFinished();
+ domains = test->getDomains();
+ g_assert(domains);
+ g_assert_cmpint(g_strv_length(domains), ==, 2);
+ g_assert(test->hasDomain(kFirstPartyDomain));
+ g_assert(test->hasDomain(kThirdPartyDomain));
+ test->deleteAllCookies();
+
+ test->setAcceptPolicy(WEBKIT_COOKIE_POLICY_ACCEPT_NEVER);
+ g_assert_cmpint(test->getAcceptPolicy(), ==, WEBKIT_COOKIE_POLICY_ACCEPT_NEVER);
+ test->loadURI(kServer->getURIForPath("/index.html").data());
+ test->waitUntilLoadFinished();
+ domains = test->getDomains();
+ g_assert(domains);
+ g_assert_cmpint(g_strv_length(domains), ==, 0);
+}
+
+static void testCookieManagerDeleteCookies(CookieManagerTest* test, gconstpointer)
+{
+ test->setAcceptPolicy(WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS);
+ test->loadURI(kServer->getURIForPath("/index.html").data());
+ test->waitUntilLoadFinished();
+ g_assert_cmpint(g_strv_length(test->getDomains()), ==, 2);
+
+ // Delete first party cookies.
+ test->deleteCookiesForDomain(kFirstPartyDomain);
+ g_assert_cmpint(g_strv_length(test->getDomains()), ==, 1);
+
+ // Delete third party cookies.
+ test->deleteCookiesForDomain(kThirdPartyDomain);
+ g_assert_cmpint(g_strv_length(test->getDomains()), ==, 0);
+
+ test->loadURI(kServer->getURIForPath("/index.html").data());
+ test->waitUntilLoadFinished();
+ g_assert_cmpint(g_strv_length(test->getDomains()), ==, 2);
+
+ // Delete all cookies.
+ test->deleteAllCookies();
+ g_assert_cmpint(g_strv_length(test->getDomains()), ==, 0);
+}
+
+static void testCookieManagerCookiesChanged(CookieManagerTest* test, gconstpointer)
+{
+ g_assert(!test->m_cookiesChanged);
+ test->setAcceptPolicy(WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS);
+ test->loadURI(kServer->getURIForPath("/index.html").data());
+ test->waitUntilLoadFinished();
+ g_assert(test->m_cookiesChanged);
+
+ test->deleteCookiesForDomain(kFirstPartyDomain);
+ test->waitUntilCookiesChanged();
+ g_assert(test->m_cookiesChanged);
+
+ test->deleteAllCookies();
+ test->waitUntilCookiesChanged();
+ g_assert(test->m_cookiesChanged);
+}
+
+static void testCookieManagerPersistentStorage(CookieManagerTest* test, gconstpointer)
+{
+ test->setAcceptPolicy(WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS);
+
+ // Text storage using a new file.
+ test->setPersistentStorage(WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT);
+ char** domains = test->getDomains();
+ g_assert(domains);
+ g_assert_cmpint(g_strv_length(domains), ==, 0);
+
+ test->loadURI(kServer->getURIForPath("/index.html").data());
+ test->waitUntilLoadFinished();
+ g_assert(test->m_cookiesChanged);
+ domains = test->getDomains();
+ g_assert(domains);
+ g_assert_cmpint(g_strv_length(domains), ==, 2);
+
+
+ // SQLite storage using a new file.
+ test->setPersistentStorage(WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE);
+ domains = test->getDomains();
+ g_assert(domains);
+ g_assert_cmpint(g_strv_length(domains), ==, 0);
+
+ test->loadURI(kServer->getURIForPath("/index.html").data());
+ test->waitUntilLoadFinished();
+ g_assert(test->m_cookiesChanged);
+ domains = test->getDomains();
+ g_assert(domains);
+ g_assert_cmpint(g_strv_length(domains), ==, 2);
+
+ // Text storage using an existing file.
+ test->setPersistentStorage(WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT);
+ domains = test->getDomains();
+ g_assert(domains);
+ g_assert_cmpint(g_strv_length(domains), ==, 2);
+ test->deleteAllCookies();
+ g_assert_cmpint(g_strv_length(test->getDomains()), ==, 0);
+
+ // SQLite storage with an existing file.
+ test->setPersistentStorage(WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE);
+ domains = test->getDomains();
+ g_assert(domains);
+ g_assert_cmpint(g_strv_length(domains), ==, 2);
+ test->deleteAllCookies();
+ g_assert_cmpint(g_strv_length(test->getDomains()), ==, 0);
+}
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ if (g_str_equal(path, "/index.html")) {
+ char* indexHtml = g_strdup_printf(kIndexHtmlFormat, soup_server_get_port(server));
+ soup_message_headers_replace(message->response_headers, "Set-Cookie", "foo=bar; Max-Age=60");
+ soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, indexHtml, strlen(indexHtml));
+ } else if (g_str_equal(path, "/image.png"))
+ soup_message_headers_replace(message->response_headers, "Set-Cookie", "baz=qux; Max-Age=60");
+ else
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+ soup_message_body_complete(message->response_body);
+}
+
+void beforeAll()
+{
+ kServer = new WebKitTestServer();
+ kServer->run(serverCallback);
+
+ CookieManagerTest::add("WebKitCookieManager", "accept-policy", testCookieManagerAcceptPolicy);
+ CookieManagerTest::add("WebKitCookieManager", "delete-cookies", testCookieManagerDeleteCookies);
+ CookieManagerTest::add("WebKitCookieManager", "cookies-changed", testCookieManagerCookiesChanged);
+ CookieManagerTest::add("WebKitCookieManager", "persistent-storage", testCookieManagerPersistentStorage);
+}
+
+void afterAll()
+{
+ delete kServer;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMDOMWindow.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMDOMWindow.cpp
new file mode 100644
index 000000000..98c6e1064
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMDOMWindow.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebProcessTestRunner.h"
+#include "WebViewTest.h"
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+#define HTML_DOCUMENT "<html><head><title></title></head><style type='text/css'>#test { font-size: 16px; }</style><body><p id='test'>test</p></body></html>"
+
+typedef struct {
+ gboolean loaded;
+ gboolean clicked;
+ WebProcessTestRunner* testRunner;
+ WebViewTest* test;
+} DomDomWindowTestStatus;
+
+static DomDomWindowTestStatus status;
+
+static void signalsNotifyCallback(const gchar *key, const gchar *value, gconstpointer)
+{
+ if (g_str_equal(key, "ready")) {
+ // The document was already loaded in the webprocess, and its "load"
+ // signal couldn't be captured on time (was issued before the test
+ // started). We load it again to force a new "load" signal in the
+ // webprocess, which will be captured this time
+ status.test->loadHtml(HTML_DOCUMENT, 0);
+ }
+
+ if (g_str_equal(key, "loaded")) {
+ status.loaded = TRUE;
+ status.test->showInWindowAndWaitUntilMapped();
+
+ // Click in a known location where the text is
+ status.test->clickMouseButton(20, 18, 1, 0);
+ }
+
+ if (g_str_equal(key, "clicked"))
+ status.clicked = TRUE;
+
+ if (g_str_equal(key, "finish")) {
+ status.test = 0;
+ status.testRunner->finishTest(status.loaded && status.clicked);
+ }
+}
+
+static void dispatchEventNotifyCallback(const gchar *key, const gchar *value, gconstpointer)
+{
+ if (g_str_equal(key, "ready")) {
+ // The document was already loaded in the webprocess, and its "load"
+ // signal couldn't be captured on time (was issued before the test
+ // started). We load it again to force a new "load" signal in the
+ // webprocess, which will be captured this time
+ status.test->loadHtml(HTML_DOCUMENT, 0);
+ }
+
+ if (g_str_equal(key, "loaded"))
+ status.loaded = TRUE;
+
+ if (g_str_equal(key, "clicked"))
+ status.clicked = TRUE;
+
+ if (g_str_equal(key, "finish")) {
+ status.test = 0;
+ status.testRunner->finishTest(status.loaded && status.clicked);
+ }
+}
+
+static void testWebKitDOMDOMWindowSignals(WebViewTest* test, gconstpointer)
+{
+ status.loaded = FALSE;
+ status.clicked = FALSE;
+ status.test = test;
+
+ status.testRunner->setNotifyCallback(G_CALLBACK(signalsNotifyCallback), 0);
+
+ // The HTML document will we loaded later, when the test is "ready" because
+ // we want to test the "load" signal
+
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add(&builder, "{sv}", "pageID", g_variant_new_uint64(webkit_web_view_get_page_id(status.test->m_webView)));
+ status.testRunner->runTestAndWait("WebKitDOMDOMWindow", "signals", g_variant_builder_end(&builder));
+ g_assert(status.testRunner->getTestResult());
+}
+
+static void testWebKitDOMDOMWindowDispatchEvent(WebViewTest* test, gconstpointer)
+{
+ status.loaded = FALSE;
+ status.clicked = FALSE;
+ status.test = test;
+
+ status.testRunner->setNotifyCallback(G_CALLBACK(dispatchEventNotifyCallback), 0);
+
+ // The HTML document will we loaded later, when the test is "ready" because
+ // we want to test the "load" signal
+
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add(&builder, "{sv}", "pageID", g_variant_new_uint64(webkit_web_view_get_page_id(status.test->m_webView)));
+ status.testRunner->runTestAndWait("WebKitDOMDOMWindow", "dispatch-event", g_variant_builder_end(&builder));
+ g_assert(status.testRunner->getTestResult());
+}
+
+static void testWebKitDOMDOMWindowGetComputedStyle(WebViewTest* test, gconstpointer)
+{
+ status.loaded = FALSE;
+ status.clicked = FALSE;
+ status.test = test;
+
+ static const char* testHTML = HTML_DOCUMENT;
+ status.test->loadHtml(testHTML, 0);
+ status.test->waitUntilLoadFinished();
+
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add(&builder, "{sv}", "pageID", g_variant_new_uint64(webkit_web_view_get_page_id(status.test->m_webView)));
+ g_assert(status.testRunner->runTest("WebKitDOMDOMWindow", "get-computed-style", g_variant_builder_end(&builder)));
+}
+
+void beforeAll()
+{
+ status.testRunner = new WebProcessTestRunner();
+ webkit_web_context_set_web_extensions_directory(webkit_web_context_get_default(), WEBKIT_TEST_WEB_EXTENSIONS_DIR);
+
+ WebViewTest::add("WebKitDOMDOMWindow", "signals", testWebKitDOMDOMWindowSignals);
+ WebViewTest::add("WebKitDOMDOMWindow", "dispatch-event", testWebKitDOMDOMWindowDispatchEvent);
+ WebViewTest::add("WebKitDOMDOMWindow", "get-computed-style", testWebKitDOMDOMWindowGetComputedStyle);
+}
+
+void afterAll()
+{
+ delete status.testRunner;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp
new file mode 100644
index 000000000..4b280424c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebViewTest.h"
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+static void testWebKitDOMNodeHierarchyNavigation(WebViewTest* test, gconstpointer)
+{
+ static const char* testHTML = "<html><head><title>This is the title</title></head><body><p>1</p><p>2</p><p>3</p></body></html>";
+ test->loadHtml(testHTML, 0);
+ test->waitUntilLoadFinished();
+
+ g_assert(test->runWebProcessTest("WebKitDOMNode", "hierarchy-navigation"));
+}
+
+static void testWebKitDOMNodeInsertion(WebViewTest* test, gconstpointer)
+{
+ static const char* testHTML = "<html><body></body></html>";
+ test->loadHtml(testHTML, 0);
+ test->waitUntilLoadFinished();
+
+ g_assert(test->runWebProcessTest("WebKitDOMNode", "insertion"));
+}
+
+static void testWebKitDOMNodeTagNames(WebViewTest* test, gconstpointer)
+{
+ static const char* testHTML = "<html><head></head><body>"
+ "<video id='video' preload='none'>"
+ " <source src='movie.ogg' type='video/ogg'>"
+ " Your browser does not support the video tag."
+ "</video>"
+ "<video id='video2' preload='none'>"
+ " <source src='movie.ogg' type='video/ogg'>"
+ " Your browser does not support the video tag."
+ "</video>"
+ "<input type='hidden' id='test' name='finish' value='false'></body></html>";
+ test->loadHtml(testHTML, 0);
+ test->waitUntilLoadFinished();
+
+ g_assert(test->runWebProcessTest("WebKitDOMNode", "tag-names"));
+}
+
+static void testWebKitDOMObjectCache(WebViewTest* test, gconstpointer)
+{
+ static const char* testHTML = "<html><body><div id='container'><p>DOM Cache test</p><a id='link href='#'>link</a></div></body></html>";
+
+ // Run the test 3 times to make sure the DOM objects are correctly released when the
+ // document is detached from the frame for every new document created.
+ for (unsigned i = 0; i < 3; ++i) {
+ test->loadHtml(testHTML, nullptr);
+ test->waitUntilLoadFinished();
+
+ g_assert(test->runWebProcessTest("WebKitDOMNode", "dom-cache"));
+ }
+}
+
+
+void beforeAll()
+{
+ WebViewTest::add("WebKitDOMNode", "hierarchy-navigation", testWebKitDOMNodeHierarchyNavigation);
+ WebViewTest::add("WebKitDOMNode", "insertion", testWebKitDOMNodeInsertion);
+ WebViewTest::add("WebKitDOMNode", "tag-names", testWebKitDOMNodeTagNames);
+ WebViewTest::add("WebKitDOMNode", "dom-cache", testWebKitDOMObjectCache);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNodeFilter.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNodeFilter.cpp
new file mode 100644
index 000000000..812946153
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNodeFilter.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebViewTest.h"
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+static const char* testHTML = "<html id='root'><head><title>DOMNodeTreeWalker</title></head>"
+ "<body><input type='button' name='push' value='push'><input type='button' name='clear' value='clear'><br></body></html>";
+
+static void runTest(WebViewTest* test, const char* name)
+{
+ test->loadHtml(testHTML, nullptr);
+ test->waitUntilLoadFinished();
+
+ g_assert(test->runWebProcessTest("WebKitDOMNodeFilter", name));
+}
+
+static void testWebKitDOMNodeFilterTreeWalker(WebViewTest* test, gconstpointer)
+{
+ runTest(test, "tree-walker");
+}
+
+static void testWebKitDOMNodeFilterNodeIterator(WebViewTest* test, gconstpointer)
+{
+ runTest(test, "node-iterator");
+}
+
+void beforeAll()
+{
+ WebViewTest::add("WebKitDOMNodeFilter", "tree-walker", testWebKitDOMNodeFilterTreeWalker);
+ WebViewTest::add("WebKitDOMNodeFilter", "node-iterator", testWebKitDOMNodeFilterNodeIterator);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMXPathNSResolver.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMXPathNSResolver.cpp
new file mode 100644
index 000000000..ff9a77192
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMXPathNSResolver.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebViewTest.h"
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+static void testWebKitDOMXPathNSResolverNative(WebViewTest* test, gconstpointer)
+{
+ static const char* nativeXML = "<root xmlns:foo='http://www.example.org'><foo:child>SUCCESS</foo:child></root>";
+ GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new_static(nativeXML, strlen(nativeXML)));
+ test->loadBytes(bytes.get(), "text/xml", nullptr, nullptr);
+ test->waitUntilLoadFinished();
+ g_assert(test->runWebProcessTest("WebKitDOMXPathNSResolver", "native"));
+}
+
+static void testWebKitDOMXPathNSResolverCustom(WebViewTest* test, gconstpointer)
+{
+ static const char* customXML = "<root xmlns='http://www.example.com'><child>SUCCESS</child></root>";
+ GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new_static(customXML, strlen(customXML)));
+ test->loadBytes(bytes.get(), "text/xml", nullptr, nullptr);
+ test->waitUntilLoadFinished();
+ g_assert(test->runWebProcessTest("WebKitDOMXPathNSResolver", "custom"));
+}
+
+void beforeAll()
+{
+ WebViewTest::add("WebKitDOMXPathNSResolver", "native", testWebKitDOMXPathNSResolverNative);
+ WebViewTest::add("WebKitDOMXPathNSResolver", "custom", testWebKitDOMXPathNSResolverCustom);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp
new file mode 100644
index 000000000..b4f21a130
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2012, 2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebKitTestServer.h"
+#include "WebViewTest.h"
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <libsoup/soup.h>
+#include <string.h>
+#include <webkit2/webkit2.h>
+#include <wtf/Vector.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/CString.h>
+
+class DownloadTest: public Test {
+public:
+ MAKE_GLIB_TEST_FIXTURE(DownloadTest);
+
+ enum DownloadEvent {
+ Started,
+ ReceivedResponse,
+ CreatedDestination,
+ ReceivedData,
+ Failed,
+ Finished
+ };
+
+ static void receivedResponseCallback(WebKitDownload* download, GParamSpec*, DownloadTest* test)
+ {
+ g_assert(webkit_download_get_response(download));
+ test->receivedResponse(download);
+ }
+
+ static void createdDestinationCallback(WebKitDownload* download, const gchar* destination, DownloadTest* test)
+ {
+ g_assert(webkit_download_get_destination(download));
+ g_assert_cmpstr(webkit_download_get_destination(download), ==, destination);
+ GRefPtr<GFile> file = adoptGRef(g_file_new_for_uri(destination));
+ g_assert(g_file_query_exists(file.get(), nullptr));
+ test->createdDestination(download, destination);
+ }
+
+ static void receivedDataCallback(WebKitDownload* download, guint64 dataLength, DownloadTest* test)
+ {
+ test->receivedData(download, dataLength);
+ }
+
+ static void finishedCallback(WebKitDownload* download, DownloadTest* test)
+ {
+ test->finished(download);
+ }
+
+ static void failedCallback(WebKitDownload* download, GError* error, DownloadTest* test)
+ {
+ g_assert(error);
+
+ const char* destinationURI = webkit_download_get_destination(download);
+ if (destinationURI) {
+ GUniquePtr<char> tempFileURI(g_strconcat(destinationURI, ".wkdownload", nullptr));
+ GRefPtr<GFile> tempFile = adoptGRef(g_file_new_for_uri(tempFileURI.get()));
+ g_assert(!g_file_query_exists(tempFile.get(), nullptr));
+ }
+
+ test->failed(download, error);
+ }
+
+ static gboolean decideDestinationCallback(WebKitDownload* download, const gchar* suggestedFilename, DownloadTest* test)
+ {
+ g_assert(suggestedFilename);
+ test->decideDestination(download, suggestedFilename);
+ return TRUE;
+ }
+
+ static void downloadStartedCallback(WebKitWebContext* context, WebKitDownload* download, DownloadTest* test)
+ {
+ g_assert(webkit_download_get_request(download));
+ test->started(download);
+ g_signal_connect(download, "notify::response", G_CALLBACK(receivedResponseCallback), test);
+ g_signal_connect(download, "created-destination", G_CALLBACK(createdDestinationCallback), test);
+ g_signal_connect(download, "received-data", G_CALLBACK(receivedDataCallback), test);
+ g_signal_connect(download, "finished", G_CALLBACK(finishedCallback), test);
+ g_signal_connect(download, "failed", G_CALLBACK(failedCallback), test);
+ g_signal_connect(download, "decide-destination", G_CALLBACK(decideDestinationCallback), test);
+ }
+
+ DownloadTest()
+ : m_mainLoop(g_main_loop_new(nullptr, TRUE))
+ , m_downloadSize(0)
+ , m_allowOverwrite(false)
+ {
+ g_signal_connect(m_webContext.get(), "download-started", G_CALLBACK(downloadStartedCallback), this);
+ }
+
+ ~DownloadTest()
+ {
+ g_signal_handlers_disconnect_matched(m_webContext.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ g_main_loop_unref(m_mainLoop);
+ }
+
+ virtual void started(WebKitDownload* download)
+ {
+ m_downloadSize = 0;
+ m_downloadEvents.clear();
+ m_downloadEvents.append(Started);
+ }
+
+ virtual void receivedResponse(WebKitDownload* download)
+ {
+ m_downloadEvents.append(ReceivedResponse);
+ }
+
+ virtual void createdDestination(WebKitDownload* download, const char* destination)
+ {
+ m_downloadEvents.append(CreatedDestination);
+ }
+
+ virtual void receivedData(WebKitDownload* download, guint64 dataLength)
+ {
+ m_downloadSize += dataLength;
+ if (!m_downloadEvents.contains(ReceivedData))
+ m_downloadEvents.append(ReceivedData);
+ }
+
+ virtual void finished(WebKitDownload* download)
+ {
+ g_assert_cmpuint(m_downloadSize, ==, webkit_download_get_received_data_length(download));
+ m_downloadEvents.append(Finished);
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ virtual void failed(WebKitDownload* download, GError* error)
+ {
+ m_downloadEvents.append(Failed);
+ }
+
+ virtual void decideDestination(WebKitDownload* download, const gchar* suggestedFilename)
+ {
+ GUniquePtr<char> destination(g_build_filename(Test::dataDirectory(), suggestedFilename, nullptr));
+ GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0));
+ webkit_download_set_destination(download, destinationURI.get());
+ }
+
+ WebKitDownload* downloadURIAndWaitUntilFinishes(const CString& requestURI)
+ {
+ WebKitDownload* download = webkit_web_context_download_uri(m_webContext.get(), requestURI.data());
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download));
+
+ g_assert(!webkit_download_get_allow_overwrite(download));
+ webkit_download_set_allow_overwrite(download, m_allowOverwrite);
+ g_assert(webkit_download_get_allow_overwrite(download) == m_allowOverwrite);
+
+ WebKitURIRequest* request = webkit_download_get_request(download);
+ g_assert(request);
+ ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI);
+
+ g_main_loop_run(m_mainLoop);
+
+ return download;
+ }
+
+ void checkDestinationAndDeleteFile(WebKitDownload* download, const char* expectedName)
+ {
+ if (!webkit_download_get_destination(download))
+ return;
+ GRefPtr<GFile> destFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(download)));
+ GUniquePtr<char> destBasename(g_file_get_basename(destFile.get()));
+ g_assert_cmpstr(destBasename.get(), ==, expectedName);
+
+ g_file_delete(destFile.get(), 0, 0);
+ }
+
+ GMainLoop* m_mainLoop;
+ Vector<DownloadEvent> m_downloadEvents;
+ guint64 m_downloadSize;
+ bool m_allowOverwrite;
+};
+
+static GRefPtr<WebKitDownload> downloadLocalFileSuccessfully(DownloadTest* test, const char* filename)
+{
+ GUniquePtr<char> sourcePath(g_build_filename(Test::getResourcesDir().data(), filename, nullptr));
+ GRefPtr<GFile> source = adoptGRef(g_file_new_for_path(sourcePath.get()));
+ GRefPtr<GFileInfo> sourceInfo = adoptGRef(g_file_query_info(source.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0));
+ GUniquePtr<char> sourceURI(g_file_get_uri(source.get()));
+ GRefPtr<WebKitDownload> download = adoptGRef(test->downloadURIAndWaitUntilFinishes(sourceURI.get()));
+ g_assert(!webkit_download_get_web_view(download.get()));
+
+ Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
+ g_assert_cmpint(events.size(), ==, 5);
+ g_assert_cmpint(events[0], ==, DownloadTest::Started);
+ g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+ g_assert_cmpint(events[2], ==, DownloadTest::CreatedDestination);
+ g_assert_cmpint(events[3], ==, DownloadTest::ReceivedData);
+ g_assert_cmpint(events[4], ==, DownloadTest::Finished);
+
+ WebKitURIRequest* request = webkit_download_get_request(download.get());
+ g_assert(request);
+ g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, sourceURI.get());
+
+ g_assert_cmpint(test->m_downloadSize, ==, g_file_info_get_size(sourceInfo.get()));
+ g_assert(webkit_download_get_destination(download.get()));
+ g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1);
+
+ return download;
+}
+
+static void testDownloadLocalFile(DownloadTest* test, gconstpointer)
+{
+ static const char* filename = "test.pdf";
+ GRefPtr<WebKitDownload> download = downloadLocalFileSuccessfully(test, filename);
+ test->checkDestinationAndDeleteFile(download.get(), filename);
+}
+
+static void createFileAtDestination(const char* filename)
+{
+ GUniquePtr<char> path(g_build_filename(Test::dataDirectory(), filename, nullptr));
+ GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(path.get()));
+ GUniqueOutPtr<GError> error;
+ g_file_create(file.get(), G_FILE_CREATE_NONE, nullptr, &error.outPtr());
+ g_assert(!error);
+ g_assert(g_file_query_exists(file.get(), nullptr));
+}
+
+static void testDownloadOverwriteDestinationAllowed(DownloadTest* test, gconstpointer)
+{
+ static const char* filename = "test.pdf";
+ createFileAtDestination(filename);
+
+ test->m_allowOverwrite = true;
+ GRefPtr<WebKitDownload> download = downloadLocalFileSuccessfully(test, filename);
+ test->checkDestinationAndDeleteFile(download.get(), filename);
+}
+
+class DownloadErrorTest: public DownloadTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(DownloadErrorTest);
+
+ enum ExpectedError {
+ NetworkError,
+ DownloadCancelled,
+ InvalidDestination,
+ DestinationExists
+ };
+
+ DownloadErrorTest()
+ : m_expectedError(NetworkError)
+ {
+ }
+
+ void receivedResponse(WebKitDownload* download)
+ {
+ DownloadTest::receivedResponse(download);
+ }
+
+ void createdDestination(WebKitDownload* download, const char* destination)
+ {
+ if (m_expectedError == DownloadCancelled)
+ webkit_download_cancel(download);
+ else
+ g_assert_not_reached();
+ }
+
+ void failed(WebKitDownload* download, GError* error)
+ {
+ g_assert(g_error_matches(error, WEBKIT_DOWNLOAD_ERROR, expectedErrorToWebKitDownloadError(m_expectedError)));
+ DownloadTest::failed(download, error);
+ }
+
+ void decideDestination(WebKitDownload* download, const gchar* suggestedFilename)
+ {
+ if (m_expectedError != InvalidDestination) {
+ DownloadTest::decideDestination(download, suggestedFilename);
+ return;
+ }
+ webkit_download_set_destination(download, "file:///foo/bar");
+ }
+
+ static WebKitDownloadError expectedErrorToWebKitDownloadError(ExpectedError expected)
+ {
+ switch (expected) {
+ case NetworkError:
+ return WEBKIT_DOWNLOAD_ERROR_NETWORK;
+ case DownloadCancelled:
+ return WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER;
+ case InvalidDestination:
+ return WEBKIT_DOWNLOAD_ERROR_DESTINATION;
+ case DestinationExists:
+ return WEBKIT_DOWNLOAD_ERROR_DESTINATION;
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ ExpectedError m_expectedError;
+};
+
+static void testDownloadOverwriteDestinationDisallowed(DownloadErrorTest* test, gconstpointer)
+{
+ static const char* filename = "test.pdf";
+ createFileAtDestination(filename);
+
+ test->m_expectedError = DownloadErrorTest::DestinationExists;
+ GUniquePtr<char> sourcePath(g_build_filename(Test::getResourcesDir().data(), filename, nullptr));
+ GRefPtr<GFile> source = adoptGRef(g_file_new_for_path(sourcePath.get()));
+ GUniquePtr<char> sourceURI(g_file_get_uri(source.get()));
+ GRefPtr<WebKitDownload> download = adoptGRef(test->downloadURIAndWaitUntilFinishes(sourceURI.get()));
+ g_assert(!webkit_download_get_web_view(download.get()));
+
+ Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, DownloadTest::Started);
+ g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+ g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+ g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+ g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 0);
+
+ test->checkDestinationAndDeleteFile(download.get(), filename);
+}
+
+static void testDownloadLocalFileError(DownloadErrorTest* test, gconstpointer)
+{
+ test->m_expectedError = DownloadErrorTest::NetworkError;
+ GRefPtr<WebKitDownload> download = adoptGRef(test->downloadURIAndWaitUntilFinishes("file:///foo/bar"));
+ g_assert(!webkit_download_get_web_view(download.get()));
+
+ Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
+ g_assert_cmpint(events.size(), ==, 3);
+ g_assert_cmpint(events[0], ==, DownloadTest::Started);
+ g_assert_cmpint(events[1], ==, DownloadTest::Failed);
+ g_assert_cmpint(events[2], ==, DownloadTest::Finished);
+ events.clear();
+ g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+
+ test->m_expectedError = DownloadErrorTest::InvalidDestination;
+ GUniquePtr<char> path(g_build_filename(Test::getResourcesDir().data(), "test.pdf", nullptr));
+ GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(path.get()));
+ GUniquePtr<char> uri(g_file_get_uri(file.get()));
+ download = adoptGRef(test->downloadURIAndWaitUntilFinishes(uri.get()));
+ g_assert(!webkit_download_get_web_view(download.get()));
+
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, DownloadTest::Started);
+ g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+ g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+ g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+ events.clear();
+ g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+ test->checkDestinationAndDeleteFile(download.get(), "bar");
+
+ test->m_expectedError = DownloadErrorTest::DownloadCancelled;
+ download = adoptGRef(test->downloadURIAndWaitUntilFinishes(uri.get()));
+ g_assert(!webkit_download_get_web_view(download.get()));
+
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, DownloadTest::Started);
+ g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+ g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+ g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+ events.clear();
+ g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+ test->checkDestinationAndDeleteFile(download.get(), "test.pdf");
+}
+
+static WebKitTestServer* kServer;
+static const char* kServerSuggestedFilename = "webkit-downloaded-file";
+
+static void addContentDispositionHTTPHeaderToResponse(SoupMessage* message)
+{
+ GUniquePtr<char> contentDisposition(g_strdup_printf("filename=%s", kServerSuggestedFilename));
+ soup_message_headers_append(message->response_headers, "Content-Disposition", contentDisposition.get());
+}
+
+static gboolean writeNextChunkIdle(SoupMessage* message)
+{
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, "chunk", 5);
+ return FALSE;
+}
+
+static void writeNextChunk(SoupMessage* message)
+{
+ g_timeout_add(100, reinterpret_cast<GSourceFunc>(writeNextChunkIdle), message);
+}
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ soup_message_set_status(message, SOUP_STATUS_OK);
+
+ if (g_str_equal(path, "/cancel-after-destination")) {
+ // Use an infinite message to make sure it's cancelled before it finishes.
+ soup_message_headers_set_encoding(message->response_headers, SOUP_ENCODING_CHUNKED);
+ addContentDispositionHTTPHeaderToResponse(message);
+ g_signal_connect(message, "wrote_headers", G_CALLBACK(writeNextChunk), nullptr);
+ g_signal_connect(message, "wrote_chunk", G_CALLBACK(writeNextChunk), nullptr);
+ return;
+ }
+
+ GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), path, nullptr));
+ char* contents;
+ gsize contentsLength;
+ if (!g_file_get_contents(filePath.get(), &contents, &contentsLength, 0)) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+ soup_message_body_complete(message->response_body);
+ return;
+ }
+
+ addContentDispositionHTTPHeaderToResponse(message);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, contentsLength);
+
+ soup_message_body_complete(message->response_body);
+}
+
+static void testDownloadRemoteFile(DownloadTest* test, gconstpointer)
+{
+ GRefPtr<WebKitDownload> download = adoptGRef(test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/test.pdf")));
+ g_assert(!webkit_download_get_web_view(download.get()));
+
+ Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
+ g_assert_cmpint(events.size(), ==, 5);
+ g_assert_cmpint(events[0], ==, DownloadTest::Started);
+ g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+ g_assert_cmpint(events[2], ==, DownloadTest::CreatedDestination);
+ g_assert_cmpint(events[3], ==, DownloadTest::ReceivedData);
+ g_assert_cmpint(events[4], ==, DownloadTest::Finished);
+ events.clear();
+
+ WebKitURIRequest* request = webkit_download_get_request(download.get());
+ g_assert(request);
+ ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/test.pdf"));
+
+ g_assert(webkit_download_get_destination(download.get()));
+ g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1);
+ test->checkDestinationAndDeleteFile(download.get(), kServerSuggestedFilename);
+}
+
+static void testDownloadRemoteFileError(DownloadErrorTest* test, gconstpointer)
+{
+ test->m_expectedError = DownloadErrorTest::NetworkError;
+ GRefPtr<WebKitDownload> download = adoptGRef(test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/foo")));
+ g_assert(!webkit_download_get_web_view(download.get()));
+
+ Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, DownloadTest::Started);
+ g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+ g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+ g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+ events.clear();
+ WebKitURIResponse* response = webkit_download_get_response(download.get());
+ g_assert_cmpuint(webkit_uri_response_get_status_code(response), ==, 404);
+ g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+
+ test->m_expectedError = DownloadErrorTest::InvalidDestination;
+ download = adoptGRef(test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/test.pdf")));
+ g_assert(!webkit_download_get_web_view(download.get()));
+
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, DownloadTest::Started);
+ g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+ g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+ g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+ events.clear();
+ g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+ test->checkDestinationAndDeleteFile(download.get(), "bar");
+
+ test->m_expectedError = DownloadErrorTest::DownloadCancelled;
+ download = adoptGRef(test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/cancel-after-destination")));
+ g_assert(!webkit_download_get_web_view(download.get()));
+
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, DownloadTest::Started);
+ g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+ g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+ g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+ events.clear();
+ g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+ // Check the intermediate file is deleted when the download is cancelled.
+ GUniquePtr<char> intermediateURI(g_strdup_printf("%s.wkdownload", webkit_download_get_destination(download.get())));
+ GRefPtr<GFile> intermediateFile = adoptGRef(g_file_new_for_uri(intermediateURI.get()));
+ g_assert(!g_file_query_exists(intermediateFile.get(), nullptr));
+}
+
+class WebViewDownloadTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(WebViewDownloadTest);
+
+ static void downloadStartedCallback(WebKitWebContext* context, WebKitDownload* download, WebViewDownloadTest* test)
+ {
+ test->m_download = download;
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download));
+ test->quitMainLoop();
+ }
+
+ WebViewDownloadTest()
+ {
+ g_signal_connect(webkit_web_view_get_context(m_webView), "download-started", G_CALLBACK(downloadStartedCallback), this);
+ }
+
+ ~WebViewDownloadTest()
+ {
+ g_signal_handlers_disconnect_matched(webkit_web_view_get_context(m_webView), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ void waitUntilDownloadStarted()
+ {
+ m_download = 0;
+ g_main_loop_run(m_mainLoop);
+ g_assert(m_download.get());
+ }
+
+ static gboolean downloadDecideDestinationCallback(WebKitDownload* download, const gchar* suggestedFilename, WebViewDownloadTest* test)
+ {
+ GUniquePtr<char> destination(g_build_filename(Test::dataDirectory(), suggestedFilename, nullptr));
+ GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0));
+ webkit_download_set_destination(download, destinationURI.get());
+ return TRUE;
+ }
+
+ static void downloadFinishedCallback(WebKitDownload* download, WebViewDownloadTest* test)
+ {
+ test->quitMainLoop();
+ }
+
+ void waitUntilDownloadFinished()
+ {
+ g_signal_connect(m_download.get(), "decide-destination", G_CALLBACK(downloadDecideDestinationCallback), this);
+ g_signal_connect(m_download.get(), "finished", G_CALLBACK(downloadFinishedCallback), this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ GRefPtr<WebKitDownload> m_download;
+};
+
+static void testWebViewDownloadURI(WebViewDownloadTest* test, gconstpointer)
+{
+ GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_view_download_uri(test->m_webView, kServer->getURIForPath("/test.pdf").data()));
+ test->waitUntilDownloadStarted();
+ g_assert(test->m_webView == webkit_download_get_web_view(download.get()));
+ test->waitUntilDownloadFinished();
+
+ GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(download.get())));
+ GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0));
+ g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), >, 0);
+ g_file_delete(downloadFile.get(), 0, 0);
+}
+
+class PolicyResponseDownloadTest: public WebViewDownloadTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(PolicyResponseDownloadTest);
+
+ static gboolean decidePolicyCallback(WebKitWebView* webView, WebKitPolicyDecision* decision, WebKitPolicyDecisionType type, PolicyResponseDownloadTest* test)
+ {
+ if (type != WEBKIT_POLICY_DECISION_TYPE_RESPONSE)
+ return FALSE;
+
+ webkit_policy_decision_download(decision);
+ return TRUE;
+ }
+
+ PolicyResponseDownloadTest()
+ {
+ g_signal_connect(m_webView, "decide-policy", G_CALLBACK(decidePolicyCallback), this);
+ }
+
+ ~PolicyResponseDownloadTest()
+ {
+ g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ void cancelDownloadAndWaitUntilFinished()
+ {
+ webkit_download_cancel(m_download.get());
+ waitUntilDownloadFinished();
+ m_download = 0;
+ }
+};
+
+static void testPolicyResponseDownload(PolicyResponseDownloadTest* test, gconstpointer)
+{
+ // Test that a download started by the the policy checker contains the web view.
+ CString requestURI = kServer->getURIForPath("/test.pdf").data();
+ test->loadURI(requestURI.data());
+ test->waitUntilDownloadStarted();
+
+ WebKitURIRequest* request = webkit_download_get_request(test->m_download.get());
+ g_assert(request);
+ ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI);
+
+ g_assert(test->m_webView == webkit_download_get_web_view(test->m_download.get()));
+ test->cancelDownloadAndWaitUntilFinished();
+}
+
+void beforeAll()
+{
+ kServer = new WebKitTestServer();
+ kServer->run(serverCallback);
+
+ DownloadTest::add("Downloads", "local-file", testDownloadLocalFile);
+ DownloadTest::add("Downloads", "overwrite-destination-allowed", testDownloadOverwriteDestinationAllowed);
+ DownloadErrorTest::add("Downloads", "overwrite-destination-disallowed", testDownloadOverwriteDestinationDisallowed);
+ DownloadErrorTest::add("Downloads", "local-file-error", testDownloadLocalFileError);
+ DownloadTest::add("Downloads", "remote-file", testDownloadRemoteFile);
+ DownloadErrorTest::add("Downloads", "remote-file-error", testDownloadRemoteFileError);
+ WebViewDownloadTest::add("WebKitWebView", "download-uri", testWebViewDownloadURI);
+ PolicyResponseDownloadTest::add("Downloads", "policy-decision-download", testPolicyResponseDownload);
+}
+
+void afterAll()
+{
+ delete kServer;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestEditor.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestEditor.cpp
new file mode 100644
index 000000000..fc429d4c3
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestEditor.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebViewTest.h"
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+static void testWebKitWebEditorSelectionChanged(WebViewTest* test, gconstpointer)
+{
+ static const gchar* testHTML = "<html><body>All work and no play make Jack a dull boy.</body></html>";
+ test->loadHtml(testHTML, nullptr);
+ test->waitUntilLoadFinished();
+
+ g_assert(test->runWebProcessTest("WebKitWebEditor", "selection-changed"));
+}
+
+void beforeAll()
+{
+ WebViewTest::add("WebKitWebEditor", "selection-changed", testWebKitWebEditorSelectionChanged);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestFrame.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestFrame.cpp
new file mode 100644
index 000000000..9145ab813
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestFrame.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebViewTest.h"
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+static void webkitFrameTestRun(WebViewTest* test, const char* testName)
+{
+ static const char* testHTML = "<html><body></body></html>";
+ test->loadHtml(testHTML, 0);
+ test->waitUntilLoadFinished();
+
+ g_assert(test->runWebProcessTest("WebKitFrame", testName));
+}
+
+static void testWebKitFrameMainFrame(WebViewTest* test, gconstpointer)
+{
+ webkitFrameTestRun(test, "main-frame");
+}
+
+static void testWebKitFrameURI(WebViewTest* test, gconstpointer)
+{
+ webkitFrameTestRun(test, "uri");
+}
+
+static void testWebKitFrameJavaScriptContext(WebViewTest* test, gconstpointer)
+{
+ webkitFrameTestRun(test, "javascript-context");
+}
+
+void beforeAll()
+{
+ WebViewTest::add("WebKitFrame", "main-frame", testWebKitFrameMainFrame);
+ WebViewTest::add("WebKitFrame", "uri", testWebKitFrameURI);
+ WebViewTest::add("WebKitFrame", "javascript-context", testWebKitFrameJavaScriptContext);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspector.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspector.cpp
new file mode 100644
index 000000000..3d993e24f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspector.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebViewTest.h"
+#include <wtf/Vector.h>
+#include <wtf/glib/GRefPtr.h>
+
+class InspectorTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(InspectorTest);
+
+ enum InspectorEvents {
+ OpenWindow,
+ BringToFront,
+ Closed,
+ Attach,
+ Detach
+ };
+
+ static gboolean openWindowCallback(WebKitWebInspector*, InspectorTest* test)
+ {
+ return test->openWindow();
+ }
+
+ static gboolean bringToFrontCallback(WebKitWebInspector*, InspectorTest* test)
+ {
+ return test->bringToFront();
+ }
+
+ static void closedCallback(WebKitWebInspector*, InspectorTest* test)
+ {
+ return test->closed();
+ }
+
+ static gboolean attachCallback(WebKitWebInspector*, InspectorTest* test)
+ {
+ return test->attach();
+ }
+
+ static gboolean detachCallback(WebKitWebInspector*, InspectorTest* test)
+ {
+ return test->detach();
+ }
+
+ static const unsigned gMinimumAttachedInspectorWidth = 750;
+ static const unsigned gMinimumAttachedInspectorHeight = 250;
+
+ InspectorTest()
+ : WebViewTest()
+ , m_inspector(webkit_web_view_get_inspector(m_webView))
+ {
+ webkit_settings_set_enable_developer_extras(webkit_web_view_get_settings(m_webView), TRUE);
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_inspector));
+ g_signal_connect(m_inspector, "open-window", G_CALLBACK(openWindowCallback), this);
+ g_signal_connect(m_inspector, "bring-to-front", G_CALLBACK(bringToFrontCallback), this);
+ g_signal_connect(m_inspector, "closed", G_CALLBACK(closedCallback), this);
+ g_signal_connect(m_inspector, "attach", G_CALLBACK(attachCallback), this);
+ g_signal_connect(m_inspector, "detach", G_CALLBACK(detachCallback), this);
+ }
+
+ ~InspectorTest()
+ {
+ g_signal_handlers_disconnect_matched(m_inspector, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ virtual bool openWindow()
+ {
+ m_events.append(OpenWindow);
+ g_main_loop_quit(m_mainLoop);
+ return TRUE;
+ }
+
+ virtual bool bringToFront()
+ {
+ m_events.append(BringToFront);
+ g_main_loop_quit(m_mainLoop);
+ return FALSE;
+ }
+
+ virtual void closed()
+ {
+ m_events.append(Closed);
+ }
+
+ virtual bool attach()
+ {
+ m_events.append(Attach);
+ return TRUE;
+ }
+
+ virtual bool detach()
+ {
+ m_events.append(Detach);
+ return TRUE;
+ }
+
+
+ static gboolean showIdle(InspectorTest* test)
+ {
+ webkit_web_inspector_show(test->m_inspector);
+ return FALSE;
+ }
+
+ void show()
+ {
+ g_idle_add(reinterpret_cast<GSourceFunc>(showIdle), this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ static void canAttachChanged(InspectorTest* test)
+ {
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ void resizeViewAndAttach()
+ {
+ // Resize the view to make room for the inspector.
+ if (!webkit_web_inspector_get_can_attach(m_inspector)) {
+ unsigned long handler = g_signal_connect_swapped(m_inspector, "notify::can-attach", G_CALLBACK(canAttachChanged), this);
+ resizeView(gMinimumAttachedInspectorWidth, (gMinimumAttachedInspectorHeight + 1) * 4 / 3);
+ g_main_loop_run(m_mainLoop);
+ g_signal_handler_disconnect(m_inspector, handler);
+ }
+
+ g_assert(webkit_web_inspector_get_can_attach(m_inspector));
+ webkit_web_inspector_attach(m_inspector);
+ }
+
+ static gboolean detachIdle(InspectorTest* test)
+ {
+ webkit_web_inspector_detach(test->m_inspector);
+ return FALSE;
+ }
+
+ void detachAndWaitUntilWindowOpened()
+ {
+ g_idle_add(reinterpret_cast<GSourceFunc>(detachIdle), this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ void close()
+ {
+ webkit_web_inspector_close(m_inspector);
+ }
+
+ WebKitWebInspector* m_inspector;
+ Vector<InspectorEvents> m_events;
+};
+
+static void testInspectorDefault(InspectorTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
+ test->resizeView(200, 200);
+ test->loadHtml("<html><body><p>WebKitGTK+ Inspector test</p></body></html>", 0);
+ test->waitUntilLoadFinished();
+
+ test->show();
+ // We don't add the view to a container, so consume the weak ref with GRefPtr.
+ GRefPtr<WebKitWebViewBase> inspectorView = webkit_web_inspector_get_web_view(test->m_inspector);
+ g_assert(inspectorView.get());
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(inspectorView.get()));
+ g_assert(!webkit_web_inspector_is_attached(test->m_inspector));
+ g_assert_cmpuint(webkit_web_inspector_get_attached_height(test->m_inspector), ==, 0);
+ g_assert(!webkit_web_inspector_get_can_attach(test->m_inspector));
+ Vector<InspectorTest::InspectorEvents>& events = test->m_events;
+ g_assert_cmpint(events.size(), ==, 1);
+ g_assert_cmpint(events[0], ==, InspectorTest::OpenWindow);
+ test->m_events.clear();
+
+ test->show();
+ events = test->m_events;
+ g_assert_cmpint(events.size(), ==, 1);
+ g_assert_cmpint(events[0], ==, InspectorTest::BringToFront);
+ test->m_events.clear();
+
+ test->resizeViewAndAttach();
+ g_assert(webkit_web_inspector_is_attached(test->m_inspector));
+ g_assert_cmpuint(webkit_web_inspector_get_attached_height(test->m_inspector), >=, InspectorTest::gMinimumAttachedInspectorHeight);
+ events = test->m_events;
+ g_assert_cmpint(events.size(), ==, 1);
+ g_assert_cmpint(events[0], ==, InspectorTest::Attach);
+ test->m_events.clear();
+
+ test->detachAndWaitUntilWindowOpened();
+ g_assert(!webkit_web_inspector_is_attached(test->m_inspector));
+ events = test->m_events;
+ g_assert_cmpint(events.size(), ==, 2);
+ g_assert_cmpint(events[0], ==, InspectorTest::Detach);
+ g_assert_cmpint(events[1], ==, InspectorTest::OpenWindow);
+ test->m_events.clear();
+
+ test->close();
+ events = test->m_events;
+ g_assert_cmpint(events.size(), ==, 1);
+ g_assert_cmpint(events[0], ==, InspectorTest::Closed);
+ test->m_events.clear();
+}
+
+class CustomInspectorTest: public InspectorTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(CustomInspectorTest);
+
+ CustomInspectorTest()
+ : InspectorTest()
+ , m_inspectorWindow(0)
+ {
+ }
+
+ ~CustomInspectorTest()
+ {
+ if (m_inspectorWindow)
+ gtk_widget_destroy(m_inspectorWindow);
+ }
+
+ bool openWindow()
+ {
+ g_assert(!m_inspectorWindow);
+ m_inspectorWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ WebKitWebViewBase* inspectorView = webkit_web_inspector_get_web_view(m_inspector);
+ g_assert(inspectorView);
+ gtk_container_add(GTK_CONTAINER(m_inspectorWindow), GTK_WIDGET(inspectorView));
+ gtk_widget_show_all(m_inspectorWindow);
+
+ return InspectorTest::openWindow();
+ }
+
+ void closed()
+ {
+ if (m_inspectorWindow) {
+ gtk_widget_destroy(m_inspectorWindow);
+ m_inspectorWindow = 0;
+ }
+
+ return InspectorTest::closed();
+ }
+
+ bool attach()
+ {
+ GRefPtr<WebKitWebViewBase> inspectorView = webkit_web_inspector_get_web_view(m_inspector);
+ if (m_inspectorWindow) {
+ gtk_container_remove(GTK_CONTAINER(m_inspectorWindow), GTK_WIDGET(inspectorView.get()));
+ gtk_widget_destroy(m_inspectorWindow);
+ m_inspectorWindow = 0;
+ }
+
+ GtkWidget* pane;
+ if (gtk_bin_get_child(GTK_BIN(m_parentWindow)) == GTK_WIDGET(m_webView)) {
+ GRefPtr<WebKitWebView> inspectedView = m_webView;
+ gtk_container_remove(GTK_CONTAINER(m_parentWindow), GTK_WIDGET(m_webView));
+ pane = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
+ gtk_paned_add1(GTK_PANED(pane), GTK_WIDGET(m_webView));
+ gtk_container_add(GTK_CONTAINER(m_parentWindow), pane);
+ gtk_widget_show_all(pane);
+ } else
+ pane = gtk_bin_get_child(GTK_BIN(m_parentWindow));
+ gtk_paned_set_position(GTK_PANED(pane), webkit_web_inspector_get_attached_height(m_inspector));
+ gtk_paned_add2(GTK_PANED(pane), GTK_WIDGET(inspectorView.get()));
+
+ return InspectorTest::attach();
+ }
+
+ bool detach()
+ {
+ GRefPtr<WebKitWebViewBase> inspectorView = webkit_web_inspector_get_web_view(m_inspector);
+ GtkWidget* pane = gtk_bin_get_child(GTK_BIN(m_parentWindow));
+ g_assert(GTK_IS_PANED(pane));
+ gtk_container_remove(GTK_CONTAINER(pane), GTK_WIDGET(inspectorView.get()));
+ return InspectorTest::detach();
+ }
+
+ void destroyWindow()
+ {
+ g_assert(m_inspectorWindow);
+ gtk_widget_destroy(m_inspectorWindow);
+ m_inspectorWindow = 0;
+ }
+
+ GtkWidget* m_inspectorWindow;
+};
+
+static void testInspectorManualAttachDetach(CustomInspectorTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
+ test->resizeView(200, 200);
+ test->loadHtml("<html><body><p>WebKitGTK+ Inspector test</p></body></html>", 0);
+ test->waitUntilLoadFinished();
+
+ test->show();
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_web_inspector_get_web_view(test->m_inspector)));
+ g_assert(!webkit_web_inspector_is_attached(test->m_inspector));
+ Vector<InspectorTest::InspectorEvents>& events = test->m_events;
+ g_assert_cmpint(events.size(), ==, 1);
+ g_assert_cmpint(events[0], ==, InspectorTest::OpenWindow);
+ test->m_events.clear();
+
+ test->resizeViewAndAttach();
+ g_assert(webkit_web_inspector_is_attached(test->m_inspector));
+ g_assert_cmpuint(webkit_web_inspector_get_attached_height(test->m_inspector), >=, InspectorTest::gMinimumAttachedInspectorHeight);
+ events = test->m_events;
+ g_assert_cmpint(events.size(), ==, 1);
+ g_assert_cmpint(events[0], ==, InspectorTest::Attach);
+ test->m_events.clear();
+
+ test->detachAndWaitUntilWindowOpened();
+ g_assert(!webkit_web_inspector_is_attached(test->m_inspector));
+ events = test->m_events;
+ g_assert_cmpint(events.size(), ==, 2);
+ g_assert_cmpint(events[0], ==, InspectorTest::Detach);
+ g_assert_cmpint(events[1], ==, InspectorTest::OpenWindow);
+ test->m_events.clear();
+
+ test->resizeViewAndAttach();
+ g_assert(webkit_web_inspector_is_attached(test->m_inspector));
+ test->m_events.clear();
+ test->close();
+ events = test->m_events;
+ g_assert_cmpint(events.size(), ==, 2);
+ g_assert_cmpint(events[0], ==, InspectorTest::Detach);
+ g_assert_cmpint(events[1], ==, InspectorTest::Closed);
+ test->m_events.clear();
+}
+
+static void testInspectorCustomContainerDestroyed(CustomInspectorTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
+ test->resizeView(200, 200);
+ test->loadHtml("<html><body><p>WebKitGTK+ Inspector test</p></body></html>", 0);
+ test->waitUntilLoadFinished();
+
+ test->show();
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_web_inspector_get_web_view(test->m_inspector)));
+ g_assert(!webkit_web_inspector_is_attached(test->m_inspector));
+
+ test->m_events.clear();
+ test->destroyWindow();
+ Vector<InspectorTest::InspectorEvents>& events = test->m_events;
+ g_assert_cmpint(events.size(), ==, 1);
+ g_assert_cmpint(events[0], ==, InspectorTest::Closed);
+ test->m_events.clear();
+}
+
+void beforeAll()
+{
+ InspectorTest::add("WebKitWebInspector", "default", testInspectorDefault);
+ CustomInspectorTest::add("WebKitWebInspector", "manual-attach-detach", testInspectorManualAttachDetach);
+ CustomInspectorTest::add("WebKitWebInspector", "custom-container-destroyed", testInspectorCustomContainerDestroyed);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspectorServer.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspectorServer.cpp
new file mode 100644
index 000000000..43f4d4817
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspectorServer.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Ltd. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "WebViewTest.h"
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/text/WTFString.h>
+
+// Name of the test server application creating the webView object.
+static const char* gTestServerAppName = "InspectorTestServer";
+
+// Max seconds to wait for the test server before inspecting it.
+static const int gMaxWaitForChild = 5;
+
+// The PID for the test server running, so we can kill it if needed.
+static GPid gChildProcessPid = 0;
+
+// Whether the child has replied and it's ready.
+static bool gChildIsReady = false;
+
+static void stopTestServer()
+{
+ // Do nothing if there's no server running.
+ if (!gChildProcessPid)
+ return;
+
+ g_spawn_close_pid(gChildProcessPid);
+ kill(gChildProcessPid, SIGTERM);
+ gChildProcessPid = 0;
+}
+
+static void sigAbortHandler(int sigNum)
+{
+ // Just stop the test server if SIGABRT was received.
+ stopTestServer();
+}
+
+static gpointer testServerMonitorThreadFunc(gpointer)
+{
+ // Wait for the specified timeout to happen.
+ g_usleep(gMaxWaitForChild * G_USEC_PER_SEC);
+
+ // Kill the child process if not ready yet.
+ if (!gChildIsReady)
+ stopTestServer();
+
+ g_thread_exit(0);
+ return 0;
+}
+
+static void startTestServerMonitor()
+{
+ gChildIsReady = false;
+ g_thread_new("TestServerMonitor", testServerMonitorThreadFunc, 0);
+}
+
+static void startTestServer()
+{
+ // Prepare argv[] for spawning the server process.
+ GUniquePtr<char> testServerPath(g_build_filename(WEBKIT_EXEC_PATH, "TestWebKitAPI", "WebKit2Gtk", gTestServerAppName, NULL));
+
+ // We install a handler to ensure that we kill the child process
+ // if the parent dies because of whatever the reason is.
+ signal(SIGABRT, sigAbortHandler);
+
+ char* testServerArgv[2];
+ testServerArgv[0] = testServerPath.get();
+ testServerArgv[1] = 0;
+
+ // Spawn the server, getting its stdout file descriptor to set a
+ // communication channel, so we know when it's ready.
+ int childStdout = 0;
+ g_assert(g_spawn_async_with_pipes(0, testServerArgv, 0, static_cast<GSpawnFlags>(0), 0, 0,
+ &gChildProcessPid, 0, &childStdout, 0, 0));
+
+ // Start monitoring the test server (in a separate thread) to
+ // ensure we don't block on the child process more than a timeout.
+ startTestServerMonitor();
+
+ char msg[2];
+ GIOChannel* ioChannel = g_io_channel_unix_new(childStdout);
+ if (g_io_channel_read_chars(ioChannel, msg, 2, 0, 0) == G_IO_STATUS_NORMAL) {
+ // Check whether the server sent a message saying it's ready
+ // and store the result globally, so the monitor can see it.
+ gChildIsReady = msg[0] == 'O' && msg[1] == 'K';
+ }
+ g_io_channel_unref(ioChannel);
+ close(childStdout);
+
+ // The timeout was reached and the server is not ready yet, so
+ // stop it inmediately, and let the unit tests fail.
+ if (!gChildIsReady)
+ stopTestServer();
+}
+
+class InspectorServerTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(InspectorServerTest);
+
+ InspectorServerTest()
+ : WebViewTest()
+ {
+ }
+
+ bool getPageList()
+ {
+ loadHtml("<script type=\"text/javascript\">\n"
+ "var pages;\n"
+ "var xhr = new XMLHttpRequest;\n"
+ "xhr.open(\"GET\", \"/pagelist.json\");\n"
+ "xhr.onload = function(e) {\n"
+ "if (xhr.status == 200) {\n"
+ "pages = JSON.parse(xhr.responseText);\n"
+ "document.title = \"OK\";\n"
+ "} else \n"
+ "document.title = \"FAIL\";\n"
+ "}\n"
+ "xhr.send();\n"
+ "</script>\n",
+ "http://127.0.0.1:2999/");
+
+ waitUntilTitleChanged();
+
+ if (!strcmp(webkit_web_view_get_title(m_webView), "OK"))
+ return true;
+
+ return false;
+ }
+
+ ~InspectorServerTest()
+ {
+ }
+};
+
+// Test to get inspector server page list from the test server.
+// Should contain only one entry pointing to http://127.0.0.1:2999/webinspector/Main.html?page=1
+static void testInspectorServerPageList(InspectorServerTest* test, gconstpointer)
+{
+ GUniqueOutPtr<GError> error;
+
+ test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
+ g_assert(test->getPageList());
+
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages.length;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ g_assert_cmpint(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 1);
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].id;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ int pageId = WebViewTest::javascriptResultToNumber(javascriptResult);
+
+ GUniquePtr<char> valueString;
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].url;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "http://127.0.0.1:2999/");
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].inspectorUrl;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ String validInspectorURL = String("/Main.html?page=") + String::number(pageId);
+ ASSERT_CMP_CSTRING(valueString.get(), ==, validInspectorURL.utf8());
+}
+
+// Test sending a raw remote debugging message through our web socket server.
+// For this specific message see: http://code.google.com/chrome/devtools/docs/protocol/tot/runtime.html#command-evaluate
+static void testRemoteDebuggingMessage(InspectorServerTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
+
+ test->loadHtml("<script type=\"text/javascript\">\n"
+ "var socket = new WebSocket('ws://127.0.0.1:2999/devtools/page/1');\n"
+ "socket.onmessage = function(message) {\n"
+ "var response = JSON.parse(message.data);\n"
+ "if (response.id === 1)\n"
+ "document.title = response.result.result.value;\n"
+ "else\n"
+ "document.title = \"FAIL\";\n"
+ "}\n"
+ "socket.onopen = function() {\n"
+ "socket.send('{\"id\": 1, \"method\": \"Runtime.evaluate\", \"params\": {\"expression\": \"2 + 2\" } }');\n"
+ "}\n"
+ "</script>",
+ "http://127.0.0.1:2999/");
+ test->waitUntilTitleChanged();
+
+ g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, "4");
+}
+
+static void openRemoteDebuggingSession(InspectorServerTest* test, gconstpointer)
+{
+ // To test the whole pipeline this exploits a behavior of the inspector front-end which won't provide the page address as title unless the
+ // debugging session was established correctly through web socket.
+ // In our case page URL should be http://127.0.0.1:2999/
+ // So this test case will fail if:
+ // - The page list didn't return a valid inspector URL
+ // - Or the front-end couldn't be loaded through the inspector HTTP server
+ // - Or the web socket connection couldn't be established between the front-end and the page through the inspector server
+ // Let's see if this test isn't raising too many false positives, in which case we should use a better predicate if available.
+
+ test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
+
+ g_assert(test->getPageList());
+
+ GUniqueOutPtr<GError> error;
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].inspectorUrl;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+
+ String resolvedURL = String("http://127.0.0.1:2999") + String::fromUTF8(WebViewTest::javascriptResultToCString(javascriptResult));
+ test->loadURI(resolvedURL.utf8().data());
+ test->waitUntilLoadFinished();
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.title", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+
+ GUniquePtr<char> title(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(title.get(), ==, "127.0.0.1");
+}
+
+static void sendIncompleteRequest(InspectorServerTest* test, gconstpointer)
+{
+ GUniqueOutPtr<GError> error;
+
+ // Connect to the inspector server.
+ GRefPtr<GSocketClient> client = adoptGRef(g_socket_client_new());
+ GRefPtr<GSocketConnection> connection = adoptGRef(g_socket_client_connect_to_host(client.get(), "127.0.0.1", 2999, 0, &error.outPtr()));
+ g_assert_no_error(error.get());
+
+ // Send incomplete request (missing blank line after headers) and check if inspector server
+ // replies. The server should not reply to an incomplete request and the test should timeout
+ // on read.
+ GOutputStream* ostream = g_io_stream_get_output_stream(G_IO_STREAM(connection.get()));
+ // Request missing blank line after headers.
+ const gchar* incompleteRequest = "GET /devtools/page/1 HTTP/1.1\r\nHost: Localhost\r\n";
+ g_output_stream_write(ostream, incompleteRequest, strlen(incompleteRequest), 0, &error.outPtr());
+ g_assert_no_error(error.get());
+
+ GInputStream* istream = g_io_stream_get_input_stream(G_IO_STREAM(connection.get()));
+ char response[16];
+ memset(response, 0, sizeof(response));
+ GRefPtr<GCancellable> cancel = adoptGRef(g_cancellable_new());
+ g_input_stream_read_async(istream, response, sizeof(response) - 1, G_PRIORITY_DEFAULT, cancel.get(), 0, 0);
+ // Give a chance for the server to reply.
+ test->wait(1);
+ g_cancellable_cancel(cancel.get());
+ // If we got any answer it means the server replied to an incomplete request, lets fail.
+ g_assert(response[0] == '\0');
+}
+
+void beforeAll()
+{
+ // Overwrite WEBKIT_INSPECTOR_SERVER variable with default IP address but different port to avoid conflict with the test inspector server page.
+ g_setenv("WEBKIT_INSPECTOR_SERVER", "127.0.0.1:2998", TRUE);
+
+ startTestServer();
+ InspectorServerTest::add("WebKitWebInspectorServer", "test-page-list", testInspectorServerPageList);
+ InspectorServerTest::add("WebKitWebInspectorServer", "test-remote-debugging-message", testRemoteDebuggingMessage);
+ InspectorServerTest::add("WebKitWebInspectorServer", "test-open-debugging-session", openRemoteDebuggingSession);
+ InspectorServerTest::add("WebKitWebInspectorServer", "test-incomplete-request", sendIncompleteRequest);
+
+}
+
+void afterAll()
+{
+ stopTestServer();
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestLoaderClient.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestLoaderClient.cpp
new file mode 100644
index 000000000..0e868cbbe
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestLoaderClient.cpp
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2009, 2010 Gustavo Noronha Silva
+ * Copyright (C) 2009, 2011 Igalia S.L.
+ * Portions Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "LoadTrackingTest.h"
+#include "WebKitTestBus.h"
+#include "WebKitTestServer.h"
+#include "WebViewTest.h"
+#include <gtk/gtk.h>
+#include <libsoup/soup.h>
+#include <wtf/text/CString.h>
+
+static WebKitTestBus* bus;
+static WebKitTestServer* kServer;
+
+const char* kDNTHeaderNotPresent = "DNT header not present";
+
+static void testLoadingStatus(LoadTrackingTest* test, gconstpointer data)
+{
+ test->setRedirectURI(kServer->getURIForPath("/normal").data());
+ test->loadURI(kServer->getURIForPath("/redirect").data());
+ test->waitUntilLoadFinished();
+
+ Vector<LoadTrackingTest::LoadEvents>& events = test->m_loadEvents;
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(events[1], ==, LoadTrackingTest::ProvisionalLoadReceivedServerRedirect);
+ g_assert_cmpint(events[2], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(events[3], ==, LoadTrackingTest::LoadFinished);
+}
+
+static void testLoadingError(LoadTrackingTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/error").data());
+ test->waitUntilLoadFinished();
+
+ Vector<LoadTrackingTest::LoadEvents>& events = test->m_loadEvents;
+ g_assert_cmpint(events.size(), ==, 3);
+ g_assert_cmpint(events[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(events[1], ==, LoadTrackingTest::ProvisionalLoadFailed);
+ g_assert_cmpint(events[2], ==, LoadTrackingTest::LoadFinished);
+}
+
+static void assertNormalLoadHappened(Vector<LoadTrackingTest::LoadEvents>& events)
+{
+ g_assert_cmpint(events.size(), ==, 3);
+ g_assert_cmpint(events[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(events[1], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(events[2], ==, LoadTrackingTest::LoadFinished);
+}
+
+static void testLoadHtml(LoadTrackingTest* test, gconstpointer)
+{
+ test->loadHtml("<html><body>Hello WebKit-GTK+</body></html>", 0);
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+}
+
+static void testLoadAlternateHTML(LoadTrackingTest* test, gconstpointer)
+{
+ test->loadAlternateHTML("<html><body>Alternate page</body></html>", "http://error-page.foo/", 0);
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+}
+
+static void testLoadAlternateHTMLForLocalPage(LoadTrackingTest* test, gconstpointer)
+{
+ test->loadAlternateHTML("<html><body>Alternate page</body></html>", "file:///not/actually/loaded.html", 0);
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+}
+
+static void testLoadPlainText(LoadTrackingTest* test, gconstpointer)
+{
+ test->loadPlainText("Hello WebKit-GTK+");
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+}
+
+static void testLoadBytes(LoadTrackingTest* test, gconstpointer)
+{
+ GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr));
+ char* contents;
+ gsize contentsLength;
+ g_file_get_contents(filePath.get(), &contents, &contentsLength, nullptr);
+ GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new_take(contents, contentsLength));
+ test->loadBytes(bytes.get(), "image/vnd.microsoft.icon", nullptr, nullptr);
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+}
+
+static void testLoadRequest(LoadTrackingTest* test, gconstpointer)
+{
+ GRefPtr<WebKitURIRequest> request(webkit_uri_request_new(kServer->getURIForPath("/normal").data()));
+ test->loadRequest(request.get());
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+}
+
+class LoadStopTrackingTest : public LoadTrackingTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(LoadStopTrackingTest);
+
+ virtual void loadCommitted()
+ {
+ LoadTrackingTest::loadCommitted();
+ webkit_web_view_stop_loading(m_webView);
+ }
+ virtual void loadFailed(const gchar* failingURI, GError* error)
+ {
+ g_assert(g_error_matches(error, WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_CANCELLED));
+ LoadTrackingTest::loadFailed(failingURI, error);
+ }
+};
+
+static void testLoadCancelled(LoadStopTrackingTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/cancelled").data());
+ test->waitUntilLoadFinished();
+
+ Vector<LoadTrackingTest::LoadEvents>& events = test->m_loadEvents;
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(events[1], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(events[2], ==, LoadTrackingTest::LoadFailed);
+ g_assert_cmpint(events[3], ==, LoadTrackingTest::LoadFinished);
+}
+
+static void testWebViewTitle(LoadTrackingTest* test, gconstpointer)
+{
+ g_assert(!webkit_web_view_get_title(test->m_webView));
+ test->loadHtml("<html><head><title>Welcome to WebKit-GTK+!</title></head></html>", 0);
+ test->waitUntilLoadFinished();
+ g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, "Welcome to WebKit-GTK+!");
+}
+
+static void testWebViewReload(LoadTrackingTest* test, gconstpointer)
+{
+ // Check that nothing happens when there's nothing to reload.
+ test->reload();
+ test->wait(0.25); // Wait for a quarter of a second.
+
+ test->loadURI(kServer->getURIForPath("/normal").data());
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+
+ test->reload();
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+}
+
+static void testLoadProgress(LoadTrackingTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/normal").data());
+ test->waitUntilLoadFinished();
+ g_assert_cmpfloat(test->m_estimatedProgress, ==, 1);
+}
+
+static void testWebViewHistoryLoad(LoadTrackingTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/normal").data());
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+
+ test->loadURI(kServer->getURIForPath("/normal2").data());
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+
+ // Check that load process is the same for pages loaded from history cache.
+ test->goBack();
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+
+ test->goForward();
+ test->waitUntilLoadFinished();
+ assertNormalLoadHappened(test->m_loadEvents);
+}
+
+class ViewURITrackingTest: public LoadTrackingTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ViewURITrackingTest);
+
+ static void uriChanged(GObject*, GParamSpec*, ViewURITrackingTest* test)
+ {
+ g_assert_cmpstr(test->m_activeURI.data(), !=, webkit_web_view_get_uri(test->m_webView));
+ test->m_activeURI = webkit_web_view_get_uri(test->m_webView);
+ }
+
+ ViewURITrackingTest()
+ : m_activeURI(webkit_web_view_get_uri(m_webView))
+ {
+ g_assert(m_activeURI.isNull());
+ g_signal_connect(m_webView, "notify::uri", G_CALLBACK(uriChanged), this);
+ }
+
+ void provisionalLoadStarted()
+ {
+ checkActiveURI("/redirect");
+ }
+
+ void provisionalLoadReceivedServerRedirect()
+ {
+ checkActiveURI("/normal");
+ }
+
+ void loadCommitted()
+ {
+ checkActiveURI("/normal");
+ }
+
+ void loadFinished()
+ {
+ checkActiveURI("/normal");
+ LoadTrackingTest::loadFinished();
+ }
+
+ CString m_activeURI;
+
+private:
+ void checkActiveURI(const char* uri)
+ {
+ ASSERT_CMP_CSTRING(m_activeURI, ==, kServer->getURIForPath(uri));
+ }
+};
+
+static void testWebViewActiveURI(ViewURITrackingTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/redirect").data());
+ test->waitUntilLoadFinished();
+}
+
+class ViewIsLoadingTest: public LoadTrackingTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ViewIsLoadingTest);
+
+ static void isLoadingChanged(GObject*, GParamSpec*, ViewIsLoadingTest* test)
+ {
+ if (webkit_web_view_is_loading(test->m_webView))
+ test->beginLoad();
+ else
+ test->endLoad();
+ }
+
+ ViewIsLoadingTest()
+ {
+ g_signal_connect(m_webView, "notify::is-loading", G_CALLBACK(isLoadingChanged), this);
+ }
+
+ void beginLoad()
+ {
+ // New load, load-started hasn't been emitted yet.
+ g_assert(m_loadEvents.isEmpty());
+ g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+ }
+
+ void endLoad()
+ {
+ // Load finish, load-finished and load-failed haven't been emitted yet.
+ g_assert(!m_loadEvents.isEmpty());
+ g_assert(!m_loadEvents.contains(LoadTrackingTest::LoadFinished));
+ g_assert(!m_loadEvents.contains(LoadTrackingTest::LoadFailed));
+ }
+};
+
+static void testWebViewIsLoading(ViewIsLoadingTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/normal").data());
+ test->waitUntilLoadFinished();
+
+ test->reload();
+ test->waitUntilLoadFinished();
+
+ test->loadURI(kServer->getURIForPath("/error").data());
+ test->waitUntilLoadFinished();
+
+ test->loadURI(kServer->getURIForPath("/normal").data());
+ test->waitUntilLoadFinished();
+ test->loadURI(kServer->getURIForPath("/normal2").data());
+ test->waitUntilLoadFinished();
+
+ test->goBack();
+ test->waitUntilLoadFinished();
+
+ test->goForward();
+ test->waitUntilLoadFinished();
+}
+
+class WebPageURITest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(WebPageURITest);
+
+ static void webPageURIChangedCallback(GDBusConnection*, const char*, const char*, const char*, const char*, GVariant* result, WebPageURITest* test)
+ {
+ const char* uri;
+ g_variant_get(result, "(&s)", &uri);
+ test->m_webPageURIs.append(uri);
+ }
+
+ static void webViewURIChanged(GObject*, GParamSpec*, WebPageURITest* test)
+ {
+ test->m_webViewURIs.append(webkit_web_view_get_uri(test->m_webView));
+ }
+
+ WebPageURITest()
+ {
+ GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID));
+ GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(),
+ "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", m_mainLoop));
+ m_uriChangedSignalID = g_dbus_connection_signal_subscribe(
+ g_dbus_proxy_get_connection(proxy.get()),
+ 0,
+ "org.webkit.gtk.WebExtensionTest",
+ "URIChanged",
+ "/org/webkit/gtk/WebExtensionTest",
+ 0,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ reinterpret_cast<GDBusSignalCallback>(webPageURIChangedCallback),
+ this,
+ 0);
+ g_assert(m_uriChangedSignalID);
+
+ g_signal_connect(m_webView, "notify::uri", G_CALLBACK(webViewURIChanged), this);
+ }
+
+ ~WebPageURITest()
+ {
+ g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ g_dbus_connection_signal_unsubscribe(bus->connection(), m_uriChangedSignalID);
+ }
+
+ unsigned m_uriChangedSignalID;
+ Vector<CString> m_webPageURIs;
+ Vector<CString> m_webViewURIs;
+};
+
+static void testWebPageURI(WebPageURITest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/redirect").data());
+ test->waitUntilLoadFinished();
+
+ g_assert_cmpint(test->m_webPageURIs.size(), ==, test->m_webViewURIs.size());
+ for (size_t i = 0; i < test->m_webPageURIs.size(); ++i)
+ ASSERT_CMP_CSTRING(test->m_webPageURIs[i], ==, test->m_webViewURIs[i]);
+
+ g_assert_cmpint(test->m_webPageURIs.size(), ==, 2);
+ ASSERT_CMP_CSTRING(test->m_webPageURIs[0], ==, kServer->getURIForPath("/redirect"));
+ ASSERT_CMP_CSTRING(test->m_webPageURIs[1], ==, kServer->getURIForPath("/normal"));
+
+}
+
+static void testURIRequestHTTPHeaders(WebViewTest* test, gconstpointer)
+{
+ GRefPtr<WebKitURIRequest> uriRequest = adoptGRef(webkit_uri_request_new("file:///foo/bar"));
+ g_assert(uriRequest.get());
+ g_assert_cmpstr(webkit_uri_request_get_uri(uriRequest.get()), ==, "file:///foo/bar");
+ g_assert(!webkit_uri_request_get_http_headers(uriRequest.get()));
+
+ // Load a request with no Do Not Track header.
+ webkit_uri_request_set_uri(uriRequest.get(), kServer->getURIForPath("/do-not-track-header").data());
+ test->loadRequest(uriRequest.get());
+ test->waitUntilLoadFinished();
+
+ size_t mainResourceDataSize = 0;
+ const char* mainResourceData = test->mainResourceData(mainResourceDataSize);
+ g_assert_cmpint(mainResourceDataSize, ==, strlen(kDNTHeaderNotPresent));
+ g_assert(!strncmp(mainResourceData, kDNTHeaderNotPresent, mainResourceDataSize));
+
+ // Add the Do Not Track header and load the request again.
+ SoupMessageHeaders* headers = webkit_uri_request_get_http_headers(uriRequest.get());
+ g_assert(headers);
+ soup_message_headers_append(headers, "DNT", "1");
+ test->loadRequest(uriRequest.get());
+ test->waitUntilLoadFinished();
+
+ mainResourceData = test->mainResourceData(mainResourceDataSize);
+ g_assert_cmpint(mainResourceDataSize, ==, 1);
+ g_assert(!strncmp(mainResourceData, "1", mainResourceDataSize));
+
+ // Load a URI for which the web extension will add the Do Not Track header.
+ test->loadURI(kServer->getURIForPath("/add-do-not-track-header").data());
+ test->waitUntilLoadFinished();
+
+ mainResourceData = test->mainResourceData(mainResourceDataSize);
+ g_assert_cmpint(mainResourceDataSize, ==, 1);
+ g_assert(!strncmp(mainResourceData, "1", mainResourceDataSize));
+}
+
+static void testURIResponseHTTPHeaders(WebViewTest* test, gconstpointer)
+{
+ test->loadHtml("<html><body>No HTTP headers</body></html>", "file:///");
+ test->waitUntilLoadFinished();
+ WebKitWebResource* resource = webkit_web_view_get_main_resource(test->m_webView);
+ g_assert(WEBKIT_IS_WEB_RESOURCE(resource));
+ WebKitURIResponse* response = webkit_web_resource_get_response(resource);
+ g_assert(WEBKIT_IS_URI_RESPONSE(response));
+ g_assert(!webkit_uri_response_get_http_headers(response));
+
+ test->loadURI(kServer->getURIForPath("/headers").data());
+ test->waitUntilLoadFinished();
+ resource = webkit_web_view_get_main_resource(test->m_webView);
+ g_assert(WEBKIT_IS_WEB_RESOURCE(resource));
+ response = webkit_web_resource_get_response(resource);
+ g_assert(WEBKIT_IS_URI_RESPONSE(response));
+ SoupMessageHeaders* headers = webkit_uri_response_get_http_headers(response);
+ g_assert(headers);
+ g_assert_cmpstr(soup_message_headers_get_one(headers, "Foo"), ==, "bar");
+}
+
+static void testRedirectToDataURI(WebViewTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/redirect-to-data").data());
+ test->waitUntilLoadFinished();
+
+ static const char* expectedData = "data-uri";
+ size_t mainResourceDataSize = 0;
+ const char* mainResourceData = test->mainResourceData(mainResourceDataSize);
+ g_assert_cmpint(mainResourceDataSize, ==, strlen(expectedData));
+ g_assert(!strncmp(mainResourceData, expectedData, mainResourceDataSize));
+}
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ static const char* responseString = "<html><body>Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
+ "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
+ "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
+ "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
+ "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
+ "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
+ "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
+ "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!</body></html>";
+
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ soup_message_set_status(message, SOUP_STATUS_OK);
+
+ if (g_str_has_prefix(path, "/normal"))
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, responseString, strlen(responseString));
+ else if (g_str_equal(path, "/error"))
+ soup_message_set_status(message, SOUP_STATUS_CANT_CONNECT);
+ else if (g_str_equal(path, "/redirect")) {
+ soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
+ soup_message_headers_append(message->response_headers, "Location", "/normal");
+ } else if (g_str_equal(path, "/cancelled")) {
+ soup_message_headers_set_encoding(message->response_headers, SOUP_ENCODING_CHUNKED);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, responseString, strlen(responseString));
+ soup_server_unpause_message(server, message);
+ return;
+ } else if (g_str_equal(path, "/do-not-track-header") || g_str_equal(path, "/add-do-not-track-header")) {
+ const char* doNotTrack = soup_message_headers_get_one(message->request_headers, "DNT");
+ if (doNotTrack)
+ soup_message_body_append(message->response_body, SOUP_MEMORY_COPY, doNotTrack, strlen(doNotTrack));
+ else
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kDNTHeaderNotPresent, strlen(kDNTHeaderNotPresent));
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ } else if (g_str_equal(path, "/headers")) {
+ soup_message_headers_append(message->response_headers, "Foo", "bar");
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, responseString, strlen(responseString));
+ } else if (g_str_equal(path, "/redirect-to-data")) {
+ soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
+ soup_message_headers_append(message->response_headers, "Location", "data:text/plain;charset=utf-8,data-uri");
+ } else
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+
+ soup_message_body_complete(message->response_body);
+}
+
+void beforeAll()
+{
+ bus = new WebKitTestBus();
+ if (!bus->run())
+ return;
+
+ kServer = new WebKitTestServer();
+ kServer->run(serverCallback);
+
+ LoadTrackingTest::add("WebKitWebView", "loading-status", testLoadingStatus);
+ LoadTrackingTest::add("WebKitWebView", "loading-error", testLoadingError);
+ LoadTrackingTest::add("WebKitWebView", "load-html", testLoadHtml);
+ LoadTrackingTest::add("WebKitWebView", "load-alternate-html", testLoadAlternateHTML);
+ LoadTrackingTest::add("WebKitWebView", "load-alternate-html-for-local-page", testLoadAlternateHTMLForLocalPage);
+ LoadTrackingTest::add("WebKitWebView", "load-plain-text", testLoadPlainText);
+ LoadTrackingTest::add("WebKitWebView", "load-bytes", testLoadBytes);
+ LoadTrackingTest::add("WebKitWebView", "load-request", testLoadRequest);
+ LoadStopTrackingTest::add("WebKitWebView", "stop-loading", testLoadCancelled);
+ LoadTrackingTest::add("WebKitWebView", "title", testWebViewTitle);
+ LoadTrackingTest::add("WebKitWebView", "progress", testLoadProgress);
+ LoadTrackingTest::add("WebKitWebView", "reload", testWebViewReload);
+ LoadTrackingTest::add("WebKitWebView", "history-load", testWebViewHistoryLoad);
+
+ // This test checks that web view notify::uri signal is correctly emitted
+ // and the uri is already updated when loader client signals are emitted.
+ ViewURITrackingTest::add("WebKitWebView", "active-uri", testWebViewActiveURI);
+
+ ViewIsLoadingTest::add("WebKitWebView", "is-loading", testWebViewIsLoading);
+ WebPageURITest::add("WebKitWebPage", "get-uri", testWebPageURI);
+ WebViewTest::add("WebKitURIRequest", "http-headers", testURIRequestHTTPHeaders);
+ WebViewTest::add("WebKitURIResponse", "http-headers", testURIResponseHTTPHeaders);
+ WebViewTest::add("WebKitWebPage", "redirect-to-data-uri", testRedirectToDataURI);
+}
+
+void afterAll()
+{
+ delete bus;
+ delete kServer;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestMultiprocess.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestMultiprocess.cpp
new file mode 100644
index 000000000..020ad3529
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestMultiprocess.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "TestMain.h"
+#include "WebKitTestBus.h"
+#include "WebViewTest.h"
+#include <webkit2/webkit2.h>
+#include <wtf/Vector.h>
+
+static const unsigned numViews = 2;
+static WebKitTestBus* bus;
+
+class MultiprocessTest: public Test {
+public:
+ MAKE_GLIB_TEST_FIXTURE(MultiprocessTest);
+
+ MultiprocessTest()
+ : m_mainLoop(g_main_loop_new(nullptr, TRUE))
+ , m_initializeWebExtensionsSignalCount(0)
+ , m_webViewBusNames(numViews)
+ , m_webViews(numViews)
+ {
+ webkit_web_context_set_process_model(m_webContext.get(), WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
+ }
+
+ void initializeWebExtensions() override
+ {
+ Test::initializeWebExtensions();
+ m_initializeWebExtensionsSignalCount++;
+ }
+
+ static void loadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, MultiprocessTest* test)
+ {
+ if (loadEvent != WEBKIT_LOAD_FINISHED)
+ return;
+ g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(loadChanged), test);
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ void loadWebViewAndWaitUntilLoaded(unsigned index)
+ {
+ g_assert_cmpuint(index, <, numViews);
+
+ m_webViews[index] = WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(m_webContext.get()));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_webViews[index].get()));
+
+ m_webViewBusNames[index] = GUniquePtr<char>(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID));
+
+ webkit_web_view_load_html(m_webViews[index].get(), "<html></html>", nullptr);
+ g_signal_connect(m_webViews[index].get(), "load-changed", G_CALLBACK(loadChanged), this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ unsigned webProcessPid(unsigned index)
+ {
+ g_assert_cmpuint(index, <, numViews);
+
+ GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(m_webViewBusNames[index].get(),
+ "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", m_mainLoop));
+
+ GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync(
+ proxy.get(),
+ "GetProcessIdentifier",
+ nullptr,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, nullptr, nullptr));
+ g_assert(result);
+
+ guint32 identifier = 0;
+ g_variant_get(result.get(), "(u)", &identifier);
+ return identifier;
+ }
+
+ static void nameVanishedCallback(GDBusConnection* connection, const gchar* name, gpointer userData)
+ {
+ g_main_loop_quit(static_cast<GMainLoop*>(userData));
+ }
+
+ void destroyWebViewAndWaitUntilWebProcessFinishes(unsigned index)
+ {
+ g_assert_cmpuint(index, <, numViews);
+
+ unsigned watcherID = g_bus_watch_name_on_connection(bus->connection(), m_webViewBusNames[index].get(), G_BUS_NAME_WATCHER_FLAGS_NONE,
+ nullptr, nameVanishedCallback, m_mainLoop, nullptr);
+ gtk_widget_destroy(GTK_WIDGET(m_webViews[index].get()));
+ g_main_loop_run(m_mainLoop);
+ g_bus_unwatch_name(watcherID);
+ }
+
+ GMainLoop* m_mainLoop;
+ unsigned m_initializeWebExtensionsSignalCount;
+ Vector<GUniquePtr<char>, numViews> m_webViewBusNames;
+ Vector<GRefPtr<WebKitWebView>, numViews> m_webViews;
+};
+
+static void testProcessPerWebView(MultiprocessTest* test, gconstpointer)
+{
+ // Create two web views. As we are in multiprocess mode, there must be
+ // two web processes, running an instance of the web extension each.
+ // The initialize-web-extensions must have been called twice, and the
+ // identifiers generated for them must be different (and their reported
+ // process identifiers).
+
+ for (unsigned i = 0; i < numViews; i++) {
+ test->loadWebViewAndWaitUntilLoaded(i);
+ g_assert(WEBKIT_IS_WEB_VIEW(test->m_webViews[i].get()));
+ g_assert(test->m_webViewBusNames[i]);
+ }
+
+ g_assert_cmpuint(test->m_initializeWebExtensionsSignalCount, ==, numViews);
+ g_assert_cmpstr(test->m_webViewBusNames[0].get(), !=, test->m_webViewBusNames[1].get());
+ g_assert_cmpuint(test->webProcessPid(0), !=, test->webProcessPid(1));
+
+ // Check that web processes finish when the web view is destroyed even when it's not finalized.
+ // See https://bugs.webkit.org/show_bug.cgi?id=129783.
+ for (unsigned i = 0; i < numViews; i++) {
+ GRefPtr<WebKitWebView> webView = test->m_webViews[i];
+ test->destroyWebViewAndWaitUntilWebProcessFinishes(i);
+ }
+}
+
+class UIClientMultiprocessTest: public Test {
+public:
+ MAKE_GLIB_TEST_FIXTURE(UIClientMultiprocessTest);
+
+ enum WebViewEvents {
+ Create,
+ ReadyToShow,
+ Close
+ };
+
+ static GtkWidget* viewCreateCallback(WebKitWebView* webView, WebKitNavigationAction*, UIClientMultiprocessTest* test)
+ {
+ return test->viewCreate(webView);
+ }
+
+ static void viewReadyToShowCallback(WebKitWebView* webView, UIClientMultiprocessTest* test)
+ {
+ test->viewReadyToShow(webView);
+ }
+
+ static void viewCloseCallback(WebKitWebView* webView, UIClientMultiprocessTest* test)
+ {
+ test->viewClose(webView);
+ }
+
+ UIClientMultiprocessTest()
+ : m_mainLoop(g_main_loop_new(nullptr, TRUE))
+ , m_initializeWebExtensionsSignalCount(0)
+ {
+ webkit_web_context_set_process_model(m_webContext.get(), WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
+ m_webView = WEBKIT_WEB_VIEW(g_object_ref_sink(webkit_web_view_new_with_context(m_webContext.get())));
+ webkit_settings_set_javascript_can_open_windows_automatically(webkit_web_view_get_settings(m_webView), TRUE);
+
+ g_signal_connect(m_webView, "create", G_CALLBACK(viewCreateCallback), this);
+ }
+
+ ~UIClientMultiprocessTest()
+ {
+ g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ gtk_widget_destroy(GTK_WIDGET(m_webView));
+ }
+
+ void initializeWebExtensions() override
+ {
+ Test::initializeWebExtensions();
+ m_initializeWebExtensionsSignalCount++;
+ }
+
+ GtkWidget* viewCreate(WebKitWebView* webView)
+ {
+ g_assert(webView == m_webView);
+
+ GtkWidget* newWebView = webkit_web_view_new_with_related_view(webView);
+ g_object_ref_sink(newWebView);
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newWebView));
+ m_webViewEvents.append(Create);
+
+ g_signal_connect(newWebView, "ready-to-show", G_CALLBACK(viewReadyToShowCallback), this);
+ g_signal_connect(newWebView, "close", G_CALLBACK(viewCloseCallback), this);
+
+ return newWebView;
+ }
+
+ void viewReadyToShow(WebKitWebView* webView)
+ {
+ g_assert(m_webView != webView);
+ m_webViewEvents.append(ReadyToShow);
+ }
+
+ void viewClose(WebKitWebView* webView)
+ {
+ g_assert(m_webView != webView);
+
+ m_webViewEvents.append(Close);
+ g_object_unref(webView);
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ void waitUntilNewWebViewClose()
+ {
+ g_main_loop_run(m_mainLoop);
+ }
+
+ WebKitWebView* m_webView;
+ GMainLoop* m_mainLoop;
+ unsigned m_initializeWebExtensionsSignalCount;
+ Vector<WebViewEvents> m_webViewEvents;
+};
+
+static void testMultiprocessWebViewCreateReadyClose(UIClientMultiprocessTest* test, gconstpointer)
+{
+ webkit_web_view_load_html(test->m_webView, "<html><body onLoad=\"window.open().close();\"></html>", nullptr);
+ test->waitUntilNewWebViewClose();
+
+ Vector<UIClientMultiprocessTest::WebViewEvents>& events = test->m_webViewEvents;
+ g_assert_cmpint(events.size(), ==, 3);
+ g_assert_cmpint(events[0], ==, UIClientMultiprocessTest::Create);
+ g_assert_cmpint(events[1], ==, UIClientMultiprocessTest::ReadyToShow);
+ g_assert_cmpint(events[2], ==, UIClientMultiprocessTest::Close);
+
+ g_assert_cmpuint(test->m_initializeWebExtensionsSignalCount, ==, 1);
+}
+
+static void testWebProcessLimit(MultiprocessTest* test, gconstpointer)
+{
+ g_assert_cmpuint(webkit_web_context_get_web_process_count_limit(test->m_webContext.get()), ==, 0);
+
+ webkit_web_context_set_web_process_count_limit(test->m_webContext.get(), 1);
+ g_assert_cmpuint(webkit_web_context_get_web_process_count_limit(test->m_webContext.get()), ==, 1);
+
+ // Create two web views but there should be only one web process.
+ for (unsigned i = 0; i < numViews; i++) {
+ test->loadWebViewAndWaitUntilLoaded(i);
+ g_assert(WEBKIT_IS_WEB_VIEW(test->m_webViews[i].get()));
+ }
+
+ g_assert_cmpuint(test->m_initializeWebExtensionsSignalCount, ==, 1);
+}
+
+void beforeAll()
+{
+ // Check that default setting is the one stated in the documentation
+ g_assert_cmpuint(webkit_web_context_get_process_model(webkit_web_context_get_default()),
+ ==, WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS);
+
+ webkit_web_context_set_process_model(webkit_web_context_get_default(),
+ WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
+
+ // Check that the getter returns the newly-set value
+ g_assert_cmpuint(webkit_web_context_get_process_model(webkit_web_context_get_default()),
+ ==, WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
+
+ bus = new WebKitTestBus();
+ if (!bus->run())
+ return;
+
+ MultiprocessTest::add("WebKitWebContext", "process-per-web-view", testProcessPerWebView);
+ UIClientMultiprocessTest::add("WebKitWebView", "multiprocess-create-ready-close", testMultiprocessWebViewCreateReadyClose);
+ MultiprocessTest::add("WebKitWebContext", "web-process-limit", testWebProcessLimit);
+}
+
+void afterAll()
+{
+ delete bus;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestPrinting.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestPrinting.cpp
new file mode 100644
index 000000000..c13185427
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestPrinting.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebViewTest.h"
+#include <glib/gstdio.h>
+#include <wtf/glib/GRefPtr.h>
+
+#ifdef HAVE_GTK_UNIX_PRINTING
+#include <gtk/gtkunixprint.h>
+#endif
+
+static void testPrintOperationPrintSettings(WebViewTest* test, gconstpointer)
+{
+ GRefPtr<WebKitPrintOperation> printOperation = adoptGRef(webkit_print_operation_new(test->m_webView));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(printOperation.get()));
+
+ g_assert(!webkit_print_operation_get_print_settings(printOperation.get()));
+ g_assert(!webkit_print_operation_get_page_setup(printOperation.get()));
+
+ GRefPtr<GtkPrintSettings> printSettings = adoptGRef(gtk_print_settings_new());
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(printSettings.get()));
+
+ GRefPtr<GtkPageSetup> pageSetup = adoptGRef(gtk_page_setup_new());
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(pageSetup.get()));
+
+ webkit_print_operation_set_print_settings(printOperation.get(), printSettings.get());
+ webkit_print_operation_set_page_setup(printOperation.get(), pageSetup.get());
+
+ g_assert(webkit_print_operation_get_print_settings(printOperation.get()) == printSettings.get());
+ g_assert(webkit_print_operation_get_page_setup(printOperation.get()) == pageSetup.get());
+}
+
+static gboolean webViewPrintCallback(WebKitWebView* webView, WebKitPrintOperation* printOperation, WebViewTest* test)
+{
+ g_assert(webView == test->m_webView);
+
+ g_assert(WEBKIT_IS_PRINT_OPERATION(printOperation));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(printOperation));
+
+ g_assert(!webkit_print_operation_get_print_settings(printOperation));
+ g_assert(!webkit_print_operation_get_page_setup(printOperation));
+
+ g_main_loop_quit(test->m_mainLoop);
+
+ return TRUE;
+}
+
+static void testWebViewPrint(WebViewTest* test, gconstpointer)
+{
+ g_signal_connect(test->m_webView, "print", G_CALLBACK(webViewPrintCallback), test);
+ test->loadHtml("<html><body onLoad=\"print();\">WebKitGTK+ printing test</body></html>", 0);
+ g_main_loop_run(test->m_mainLoop);
+}
+
+#ifdef HAVE_GTK_UNIX_PRINTING
+static gboolean testPrintOperationPrintPrinter(GtkPrinter* printer, gpointer userData)
+{
+ if (strcmp(gtk_printer_get_name(printer), "Print to File"))
+ return FALSE;
+
+ GtkPrinter** foundPrinter = static_cast<GtkPrinter**>(userData);
+ *foundPrinter = static_cast<GtkPrinter*>(g_object_ref(printer));
+ return TRUE;
+}
+
+static GtkPrinter* findPrintToFilePrinter()
+{
+ GtkPrinter* printer = 0;
+ gtk_enumerate_printers(testPrintOperationPrintPrinter, &printer, 0, TRUE);
+ return printer;
+}
+
+class PrintTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(PrintTest);
+
+ static void printFinishedCallback(WebKitPrintOperation*, PrintTest* test)
+ {
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ static void printFailedCallback(WebKitPrintOperation*, GError* error, PrintTest* test)
+ {
+ g_assert(test->m_expectedError);
+ g_assert(error);
+ g_assert(g_error_matches(error, WEBKIT_PRINT_ERROR, test->m_expectedError));
+ }
+
+ PrintTest()
+ : m_expectedError(0)
+ {
+ m_printOperation = adoptGRef(webkit_print_operation_new(m_webView));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_printOperation.get()));
+ g_signal_connect(m_printOperation.get(), "finished", G_CALLBACK(printFinishedCallback), this);
+ g_signal_connect(m_printOperation.get(), "failed", G_CALLBACK(printFailedCallback), this);
+ }
+
+ void waitUntilPrintFinished()
+ {
+ g_main_loop_run(m_mainLoop);
+ }
+
+ GRefPtr<WebKitPrintOperation> m_printOperation;
+ unsigned m_expectedError;
+};
+
+static void testPrintOperationPrint(PrintTest* test, gconstpointer)
+{
+ test->loadHtml("<html><body>WebKitGTK+ printing test</body></html>", 0);
+ test->waitUntilLoadFinished();
+
+ GRefPtr<GtkPrinter> printer = adoptGRef(findPrintToFilePrinter());
+ if (!printer) {
+ g_message("%s", "Cannot test WebKitPrintOperation/print: no suitable printer found");
+ return;
+ }
+
+ GUniquePtr<char> outputFilename(g_build_filename(Test::dataDirectory(), "webkit-print.pdf", nullptr));
+ GRefPtr<GFile> outputFile = adoptGRef(g_file_new_for_path(outputFilename.get()));
+ GUniquePtr<char> outputURI(g_file_get_uri(outputFile.get()));
+
+ GRefPtr<GtkPrintSettings> printSettings = adoptGRef(gtk_print_settings_new());
+ gtk_print_settings_set_printer(printSettings.get(), gtk_printer_get_name(printer.get()));
+ gtk_print_settings_set(printSettings.get(), GTK_PRINT_SETTINGS_OUTPUT_URI, outputURI.get());
+
+ webkit_print_operation_set_print_settings(test->m_printOperation.get(), printSettings.get());
+ webkit_print_operation_print(test->m_printOperation.get());
+ test->waitUntilPrintFinished();
+
+ GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(outputFile.get(),
+ G_FILE_ATTRIBUTE_STANDARD_SIZE "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ static_cast<GFileQueryInfoFlags>(0), 0, 0));
+ g_assert(fileInfo.get());
+ g_assert_cmpint(g_file_info_get_size(fileInfo.get()), >, 0);
+ g_assert_cmpstr(g_file_info_get_content_type(fileInfo.get()), ==, "application/pdf");
+
+ g_file_delete(outputFile.get(), 0, 0);
+}
+
+static void testPrintOperationErrors(PrintTest* test, gconstpointer)
+{
+ test->loadHtml("<html><body>WebKitGTK+ printing errors test</body></html>", 0);
+ test->waitUntilLoadFinished();
+
+ GRefPtr<GtkPrinter> printer = adoptGRef(findPrintToFilePrinter());
+ if (!printer) {
+ g_message("%s", "Cannot test WebKitPrintOperation/print: no suitable printer found");
+ return;
+ }
+
+ // General Error: invalid filename.
+ test->m_expectedError = WEBKIT_PRINT_ERROR_GENERAL;
+ GRefPtr<GtkPrintSettings> printSettings = adoptGRef(gtk_print_settings_new());
+ gtk_print_settings_set_printer(printSettings.get(), gtk_printer_get_name(printer.get()));
+ gtk_print_settings_set(printSettings.get(), GTK_PRINT_SETTINGS_OUTPUT_URI, "file:///foo/bar");
+ webkit_print_operation_set_print_settings(test->m_printOperation.get(), printSettings.get());
+ webkit_print_operation_print(test->m_printOperation.get());
+ test->waitUntilPrintFinished();
+
+ // Printer not found error.
+ test->m_expectedError = WEBKIT_PRINT_ERROR_PRINTER_NOT_FOUND;
+ gtk_print_settings_set_printer(printSettings.get(), "The fake WebKit printer");
+ webkit_print_operation_print(test->m_printOperation.get());
+ test->waitUntilPrintFinished();
+
+ // No pages to print: print even pages for a single page document.
+ test->m_expectedError = WEBKIT_PRINT_ERROR_INVALID_PAGE_RANGE;
+ gtk_print_settings_set_printer(printSettings.get(), gtk_printer_get_name(printer.get()));
+ gtk_print_settings_set_page_set(printSettings.get(), GTK_PAGE_SET_EVEN);
+ webkit_print_operation_print(test->m_printOperation.get());
+ test->waitUntilPrintFinished();
+}
+
+class CloseAfterPrintTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(CloseAfterPrintTest);
+
+ static GtkWidget* webViewCreate(WebKitWebView* webView, WebKitNavigationAction*, CloseAfterPrintTest* test)
+ {
+ return test->createWebView();
+ }
+
+ static gboolean webViewPrint(WebKitWebView* webView, WebKitPrintOperation* printOperation, CloseAfterPrintTest* test)
+ {
+ test->print(printOperation);
+ return TRUE;
+ }
+
+ static void printOperationFinished(WebKitPrintOperation* printOperation, CloseAfterPrintTest* test)
+ {
+ test->printFinished();
+ }
+
+ static void webViewClosed(WebKitWebView* webView, CloseAfterPrintTest* test)
+ {
+ gtk_widget_destroy(GTK_WIDGET(webView));
+ test->m_webViewClosed = true;
+ if (test->m_printFinished)
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ CloseAfterPrintTest()
+ : m_webViewClosed(false)
+ , m_printFinished(false)
+ {
+ webkit_settings_set_javascript_can_open_windows_automatically(webkit_web_view_get_settings(m_webView), TRUE);
+ g_signal_connect(m_webView, "create", G_CALLBACK(webViewCreate), this);
+ }
+
+ GtkWidget* createWebView()
+ {
+ GtkWidget* newWebView = webkit_web_view_new_with_context(m_webContext.get());
+ g_object_ref_sink(newWebView);
+
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newWebView));
+ g_signal_connect(newWebView, "print", G_CALLBACK(webViewPrint), this);
+ g_signal_connect(newWebView, "close", G_CALLBACK(webViewClosed), this);
+ return newWebView;
+ }
+
+ void print(WebKitPrintOperation* printOperation)
+ {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(printOperation));
+
+ GRefPtr<GtkPrinter> printer = adoptGRef(findPrintToFilePrinter());
+ if (!printer) {
+ g_message("%s", "Cannot test WebKitPrintOperation/print: no suitable printer found");
+ return;
+ }
+
+ GUniquePtr<char> outputFilename(g_build_filename(Test::dataDirectory(), "webkit-close-after-print.pdf", nullptr));
+ m_outputFile = adoptGRef(g_file_new_for_path(outputFilename.get()));
+ GUniquePtr<char> outputURI(g_file_get_uri(m_outputFile.get()));
+
+ GRefPtr<GtkPrintSettings> printSettings = adoptGRef(gtk_print_settings_new());
+ gtk_print_settings_set_printer(printSettings.get(), gtk_printer_get_name(printer.get()));
+ gtk_print_settings_set(printSettings.get(), GTK_PRINT_SETTINGS_OUTPUT_URI, outputURI.get());
+ webkit_print_operation_set_print_settings(printOperation, printSettings.get());
+
+ m_printOperation = printOperation;
+ g_signal_connect(m_printOperation.get(), "finished", G_CALLBACK(printOperationFinished), this);
+ webkit_print_operation_print(m_printOperation.get());
+ }
+
+ void printFinished()
+ {
+ m_printFinished = true;
+ m_printOperation = nullptr;
+ g_assert(m_outputFile);
+ g_file_delete(m_outputFile.get(), 0, 0);
+ m_outputFile = nullptr;
+ if (m_webViewClosed)
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ void waitUntilPrintFinishedAndViewClosed()
+ {
+ g_main_loop_run(m_mainLoop);
+ }
+
+ GRefPtr<WebKitPrintOperation> m_printOperation;
+ GRefPtr<GFile> m_outputFile;
+ bool m_webViewClosed;
+ bool m_printFinished;
+};
+
+static void testPrintOperationCloseAfterPrint(CloseAfterPrintTest* test, gconstpointer)
+{
+ test->loadHtml("<html><body onLoad=\"w = window.open();w.print();w.close();\"></body></html>", 0);
+ test->waitUntilPrintFinishedAndViewClosed();
+}
+#endif // HAVE_GTK_UNIX_PRINTING
+
+void beforeAll()
+{
+ WebViewTest::add("WebKitPrintOperation", "printing-settings", testPrintOperationPrintSettings);
+ WebViewTest::add("WebKitWebView", "print", testWebViewPrint);
+#ifdef HAVE_GTK_UNIX_PRINTING
+ PrintTest::add("WebKitPrintOperation", "print", testPrintOperationPrint);
+ PrintTest::add("WebKitPrintOperation", "print-errors", testPrintOperationErrors);
+ CloseAfterPrintTest::add("WebKitPrintOperation", "close-after-print", testPrintOperationCloseAfterPrint);
+#endif
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestResources.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestResources.cpp
new file mode 100644
index 000000000..47f666a9d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestResources.cpp
@@ -0,0 +1,858 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebKitTestServer.h"
+#include "WebViewTest.h"
+#include <wtf/Vector.h>
+#include <wtf/glib/GMainLoopSource.h>
+#include <wtf/glib/GMutexLocker.h>
+#include <wtf/glib/GRefPtr.h>
+
+static WebKitTestServer* kServer;
+
+static const char* kIndexHtml =
+ "<html><head>"
+ " <link rel='stylesheet' href='/style.css' type='text/css'>"
+ " <script language='javascript' src='/javascript.js'></script>"
+ "</head><body>WebKitGTK+ resources test</body></html>";
+
+static const char* kStyleCSS =
+ "body {"
+ " margin: 0px;"
+ " padding: 0px;"
+ " font-family: sans-serif;"
+ " background: url(/blank.ico) 0 0 no-repeat;"
+ " color: black;"
+ "}";
+
+static const char* kJavascript = "function foo () { var a = 1; }";
+
+class ResourcesTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ResourcesTest);
+
+ static void resourceSentRequestCallback(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse, ResourcesTest* test)
+ {
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
+ if (redirectResponse)
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(redirectResponse));
+ test->resourceSentRequest(resource, request, redirectResponse);
+ }
+
+ static void resourceReceivedResponseCallback(WebKitWebResource* resource, GParamSpec*, ResourcesTest* test)
+ {
+ g_assert(webkit_web_resource_get_response(resource));
+ test->resourceReceivedResponse(resource);
+ }
+
+ static void resourceReceivedDataCallback(WebKitWebResource* resource, guint64 bytesReceived, ResourcesTest* test)
+ {
+ test->resourceReceivedData(resource, bytesReceived);
+ }
+
+ static void resourceFinishedCallback(WebKitWebResource* resource, ResourcesTest* test)
+ {
+ test->resourceFinished(resource);
+ }
+
+ static void resourceFailedCallback(WebKitWebResource* resource, GError* error, ResourcesTest* test)
+ {
+ g_assert(error);
+ test->resourceFailed(resource, error);
+ }
+
+ static void resourceLoadStartedCallback(WebKitWebView* webView, WebKitWebResource* resource, WebKitURIRequest* request, ResourcesTest* test)
+ {
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(resource));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
+
+ // Ignore favicons.
+ if (g_str_has_suffix(webkit_uri_request_get_uri(request), "favicon.ico"))
+ return;
+
+ test->resourceLoadStarted(resource, request);
+ g_signal_connect(resource, "sent-request", G_CALLBACK(resourceSentRequestCallback), test);
+ g_signal_connect(resource, "notify::response", G_CALLBACK(resourceReceivedResponseCallback), test);
+ g_signal_connect(resource, "received-data", G_CALLBACK(resourceReceivedDataCallback), test);
+ g_signal_connect(resource, "finished", G_CALLBACK(resourceFinishedCallback), test);
+ g_signal_connect(resource, "failed", G_CALLBACK(resourceFailedCallback), test);
+ }
+
+ void clearSubresources()
+ {
+ g_list_free_full(m_subresources, reinterpret_cast<GDestroyNotify>(g_object_unref));
+ m_subresources = 0;
+ }
+
+ ResourcesTest()
+ : WebViewTest()
+ , m_resourcesLoaded(0)
+ , m_resourcesToLoad(0)
+ , m_resourceDataSize(0)
+ , m_subresources(0)
+ {
+ g_signal_connect(m_webView, "resource-load-started", G_CALLBACK(resourceLoadStartedCallback), this);
+ }
+
+ ~ResourcesTest()
+ {
+ clearSubresources();
+ }
+
+ virtual void resourceLoadStarted(WebKitWebResource* resource, WebKitURIRequest* request)
+ {
+ }
+
+ virtual void resourceSentRequest(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse)
+ {
+ }
+
+ virtual void resourceReceivedResponse(WebKitWebResource* resource)
+ {
+ }
+
+ virtual void resourceReceivedData(WebKitWebResource* resource, guint64 bytesReceived)
+ {
+ }
+
+ virtual void resourceFinished(WebKitWebResource* resource)
+ {
+ g_signal_handlers_disconnect_matched(resource, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ if (webkit_web_view_get_main_resource(m_webView) != resource)
+ m_subresources = g_list_prepend(m_subresources, g_object_ref(resource));
+ if (++m_resourcesLoaded == m_resourcesToLoad)
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ virtual void resourceFailed(WebKitWebResource* resource, GError* error)
+ {
+ g_assert_not_reached();
+ }
+
+ void waitUntilResourcesLoaded(size_t resourcesCount)
+ {
+ m_resourcesLoaded = 0;
+ m_resourcesToLoad = resourcesCount;
+ clearSubresources();
+ g_main_loop_run(m_mainLoop);
+ }
+
+ GList* subresources()
+ {
+ return m_subresources;
+ }
+
+ static void resourceGetDataCallback(GObject* object, GAsyncResult* result, gpointer userData)
+ {
+ size_t dataSize;
+ GUniqueOutPtr<GError> error;
+ unsigned char* data = webkit_web_resource_get_data_finish(WEBKIT_WEB_RESOURCE(object), result, &dataSize, &error.outPtr());
+ g_assert(!error.get());
+ g_assert(data);
+ g_assert_cmpint(dataSize, >, 0);
+
+ ResourcesTest* test = static_cast<ResourcesTest*>(userData);
+ test->m_resourceData.reset(reinterpret_cast<char*>(data));
+ test->m_resourceDataSize = dataSize;
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ void checkResourceData(WebKitWebResource* resource)
+ {
+ m_resourceDataSize = 0;
+ webkit_web_resource_get_data(resource, 0, resourceGetDataCallback, this);
+ g_main_loop_run(m_mainLoop);
+
+ const char* uri = webkit_web_resource_get_uri(resource);
+ if (uri == kServer->getURIForPath("/")) {
+ g_assert_cmpint(m_resourceDataSize, ==, strlen(kIndexHtml));
+ g_assert(!strncmp(m_resourceData.get(), kIndexHtml, m_resourceDataSize));
+ } else if (uri == kServer->getURIForPath("/style.css")) {
+ g_assert_cmpint(m_resourceDataSize, ==, strlen(kStyleCSS));
+ g_assert(!strncmp(m_resourceData.get(), kStyleCSS, m_resourceDataSize));
+ } else if (uri == kServer->getURIForPath("/javascript.js")) {
+ g_assert_cmpint(m_resourceDataSize, ==, strlen(kJavascript));
+ g_assert(!strncmp(m_resourceData.get(), kJavascript, m_resourceDataSize));
+ } else
+ g_assert_not_reached();
+ m_resourceData.reset();
+ }
+
+ size_t m_resourcesLoaded;
+ size_t m_resourcesToLoad;
+ GUniquePtr<char> m_resourceData;
+ size_t m_resourceDataSize;
+ GList* m_subresources;
+};
+
+static void testWebViewResources(ResourcesTest* test, gconstpointer)
+{
+ // Nothing loaded yet, there shoulnd't be resources.
+ g_assert(!webkit_web_view_get_main_resource(test->m_webView));
+ g_assert(!test->subresources());
+
+ // Load simple page without subresources.
+ test->loadHtml("<html><body>Testing WebKitGTK+</body></html>", 0);
+ test->waitUntilLoadFinished();
+ WebKitWebResource* resource = webkit_web_view_get_main_resource(test->m_webView);
+ g_assert(resource);
+ g_assert_cmpstr(webkit_web_view_get_uri(test->m_webView), ==, webkit_web_resource_get_uri(resource));
+ g_assert(!test->subresources());
+
+ // Load simple page with subresources.
+ test->loadURI(kServer->getURIForPath("/").data());
+ test->waitUntilResourcesLoaded(4);
+
+ resource = webkit_web_view_get_main_resource(test->m_webView);
+ g_assert(resource);
+ g_assert_cmpstr(webkit_web_view_get_uri(test->m_webView), ==, webkit_web_resource_get_uri(resource));
+ GList* subresources = test->subresources();
+ g_assert(subresources);
+ g_assert_cmpint(g_list_length(subresources), ==, 3);
+
+#if 0
+ // Load the same URI again.
+ // FIXME: we need a workaround for bug https://bugs.webkit.org/show_bug.cgi?id=78510.
+ test->loadURI(kServer->getURIForPath("/").data());
+ test->waitUntilResourcesLoaded(4);
+#endif
+
+ // Reload.
+ webkit_web_view_reload_bypass_cache(test->m_webView);
+ test->waitUntilResourcesLoaded(4);
+}
+
+class SingleResourceLoadTest: public ResourcesTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(SingleResourceLoadTest);
+
+ enum LoadEvents {
+ Started,
+ SentRequest,
+ Redirected,
+ ReceivedResponse,
+ ReceivedData,
+ Finished,
+ Failed
+ };
+
+ SingleResourceLoadTest()
+ : ResourcesTest()
+ , m_resourceDataReceived(0)
+ {
+ m_resourcesToLoad = 2;
+ }
+
+ void resourceLoadStarted(WebKitWebResource* resource, WebKitURIRequest* request)
+ {
+ if (resource == webkit_web_view_get_main_resource(m_webView))
+ return;
+
+ m_resourceDataReceived = 0;
+ m_resource = resource;
+ m_loadEvents.append(Started);
+ }
+
+ void resourceSentRequest(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse)
+ {
+ if (resource != m_resource)
+ return;
+
+ if (redirectResponse)
+ m_loadEvents.append(Redirected);
+ else
+ m_loadEvents.append(SentRequest);
+ }
+
+ void resourceReceivedResponse(WebKitWebResource* resource)
+ {
+ if (resource != m_resource)
+ return;
+
+ m_loadEvents.append(ReceivedResponse);
+ }
+
+ void resourceReceivedData(WebKitWebResource* resource, guint64 bytesReceived)
+ {
+ if (resource != m_resource)
+ return;
+
+ m_resourceDataReceived += bytesReceived;
+ if (!m_loadEvents.contains(ReceivedData))
+ m_loadEvents.append(ReceivedData);
+ }
+
+ void resourceFinished(WebKitWebResource* resource)
+ {
+ if (resource != m_resource) {
+ ResourcesTest::resourceFinished(resource);
+ return;
+ }
+
+ if (!m_loadEvents.contains(Failed)) {
+ WebKitURIResponse* response = webkit_web_resource_get_response(m_resource.get());
+ g_assert(response);
+ g_assert_cmpint(webkit_uri_response_get_content_length(response), ==, m_resourceDataReceived);
+ }
+ m_loadEvents.append(Finished);
+ ResourcesTest::resourceFinished(resource);
+ }
+
+ void resourceFailed(WebKitWebResource* resource, GError* error)
+ {
+ if (resource == m_resource)
+ m_loadEvents.append(Failed);
+ }
+
+ void waitUntilResourceLoadFinished()
+ {
+ m_resource = 0;
+ m_resourcesLoaded = 0;
+ g_main_loop_run(m_mainLoop);
+ }
+
+ WebKitURIResponse* waitUntilResourceLoadFinishedAndReturnURIResponse()
+ {
+ waitUntilResourceLoadFinished();
+ g_assert(m_resource);
+ return webkit_web_resource_get_response(m_resource.get());
+ }
+
+ GRefPtr<WebKitWebResource> m_resource;
+ Vector<LoadEvents> m_loadEvents;
+ guint64 m_resourceDataReceived;
+};
+
+static void testWebResourceLoading(SingleResourceLoadTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/javascript.html").data());
+ test->waitUntilResourceLoadFinished();
+ g_assert(test->m_resource);
+ Vector<SingleResourceLoadTest::LoadEvents>& events = test->m_loadEvents;
+ g_assert_cmpint(events.size(), ==, 5);
+ g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
+ g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
+ g_assert_cmpint(events[2], ==, SingleResourceLoadTest::ReceivedResponse);
+ g_assert_cmpint(events[3], ==, SingleResourceLoadTest::ReceivedData);
+ g_assert_cmpint(events[4], ==, SingleResourceLoadTest::Finished);
+ events.clear();
+
+ test->loadURI(kServer->getURIForPath("/redirected-css.html").data());
+ test->waitUntilResourceLoadFinished();
+ g_assert(test->m_resource);
+ g_assert_cmpint(events.size(), ==, 6);
+ g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
+ g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
+ g_assert_cmpint(events[2], ==, SingleResourceLoadTest::Redirected);
+ g_assert_cmpint(events[3], ==, SingleResourceLoadTest::ReceivedResponse);
+ g_assert_cmpint(events[4], ==, SingleResourceLoadTest::ReceivedData);
+ g_assert_cmpint(events[5], ==, SingleResourceLoadTest::Finished);
+ events.clear();
+
+ test->loadURI(kServer->getURIForPath("/invalid-css.html").data());
+ test->waitUntilResourceLoadFinished();
+ g_assert(test->m_resource);
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
+ g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
+ g_assert_cmpint(events[2], ==, SingleResourceLoadTest::Failed);
+ g_assert_cmpint(events[3], ==, SingleResourceLoadTest::Finished);
+ events.clear();
+}
+
+static void testWebResourceResponse(SingleResourceLoadTest* test, gconstpointer)
+{
+ // No cached resource: First load.
+ test->loadURI(kServer->getURIForPath("/javascript.html").data());
+ WebKitURIResponse* response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_OK);
+
+ // No cached resource: Second load.
+ test->loadURI(kServer->getURIForPath("/javascript.html").data());
+ response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_OK);
+
+ // No cached resource: Reload.
+ webkit_web_view_reload(test->m_webView);
+ response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_OK);
+
+ // Cached resource: First load.
+ test->loadURI(kServer->getURIForPath("/image.html").data());
+ response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_OK);
+
+ // Cached resource: Second load.
+ test->loadURI(kServer->getURIForPath("/image.html").data());
+ response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_OK);
+
+ // Cached resource: Reload.
+ webkit_web_view_reload(test->m_webView);
+ response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_NOT_MODIFIED);
+}
+
+static void testWebResourceMimeType(SingleResourceLoadTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/javascript.html").data());
+ WebKitURIResponse* response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "text/javascript");
+
+ test->loadURI(kServer->getURIForPath("/image.html").data());
+ response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "image/x-icon");
+
+ test->loadURI(kServer->getURIForPath("/redirected-css.html").data());
+ response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "text/css");
+}
+
+static void testWebResourceSuggestedFilename(SingleResourceLoadTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/javascript.html").data());
+ WebKitURIResponse* response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert_cmpstr(webkit_uri_response_get_suggested_filename(response), ==, "JavaScript.js");
+
+ test->loadURI(kServer->getURIForPath("/image.html").data());
+ response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
+ g_assert(!webkit_uri_response_get_suggested_filename(response));
+}
+
+class ResourceURITrackingTest: public SingleResourceLoadTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ResourceURITrackingTest);
+
+ ResourceURITrackingTest()
+ : SingleResourceLoadTest()
+ {
+ }
+
+ static void uriChanged(WebKitWebResource* resource, GParamSpec*, ResourceURITrackingTest* test)
+ {
+ g_assert(resource == test->m_resource.get());
+ g_assert_cmpstr(test->m_activeURI.data(), !=, webkit_web_resource_get_uri(test->m_resource.get()));
+ test->m_activeURI = webkit_web_resource_get_uri(test->m_resource.get());
+ }
+
+ void resourceLoadStarted(WebKitWebResource* resource, WebKitURIRequest* request)
+ {
+ if (resource == webkit_web_view_get_main_resource(m_webView))
+ return;
+
+ m_resource = resource;
+ m_activeURI = webkit_web_resource_get_uri(resource);
+ checkActiveURI("/redirected.css");
+ g_assert_cmpstr(m_activeURI.data(), ==, webkit_uri_request_get_uri(request));
+ g_signal_connect(resource, "notify::uri", G_CALLBACK(uriChanged), this);
+ }
+
+ void resourceSentRequest(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse)
+ {
+ if (resource != m_resource)
+ return;
+
+ if (redirectResponse)
+ checkActiveURI("/simple-style.css");
+ else
+ checkActiveURI("/redirected.css");
+ g_assert_cmpstr(m_activeURI.data(), ==, webkit_uri_request_get_uri(request));
+ }
+
+ void resourceReceivedResponse(WebKitWebResource* resource)
+ {
+ if (resource != m_resource)
+ return;
+
+ checkActiveURI("/simple-style.css");
+ }
+
+ void resourceReceivedData(WebKitWebResource* resource, guint64 bytesReceived)
+ {
+ }
+
+ void resourceFinished(WebKitWebResource* resource)
+ {
+ if (resource == m_resource)
+ checkActiveURI("/simple-style.css");
+ ResourcesTest::resourceFinished(resource);
+ }
+
+ void resourceFailed(WebKitWebResource*, GError*)
+ {
+ g_assert_not_reached();
+ }
+
+ CString m_activeURI;
+
+private:
+ void checkActiveURI(const char* uri)
+ {
+ ASSERT_CMP_CSTRING(m_activeURI, ==, kServer->getURIForPath(uri));
+ }
+};
+
+static void testWebResourceActiveURI(ResourceURITrackingTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/redirected-css.html").data());
+ test->waitUntilResourceLoadFinished();
+}
+
+static void testWebResourceGetData(ResourcesTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/").data());
+ // FIXME: this should be 4 instead of 3, but we don't get the css image resource
+ // due to bug https://bugs.webkit.org/show_bug.cgi?id=78510.
+ test->waitUntilResourcesLoaded(3);
+
+ WebKitWebResource* resource = webkit_web_view_get_main_resource(test->m_webView);
+ g_assert(resource);
+ test->checkResourceData(resource);
+
+ GList* subresources = test->subresources();
+ for (GList* item = subresources; item; item = g_list_next(item))
+ test->checkResourceData(WEBKIT_WEB_RESOURCE(item->data));
+}
+
+static void testWebViewResourcesHistoryCache(SingleResourceLoadTest* test, gconstpointer)
+{
+ CString javascriptURI = kServer->getURIForPath("/javascript.html");
+ test->loadURI(javascriptURI.data());
+ test->waitUntilResourceLoadFinished();
+ WebKitWebResource* resource = webkit_web_view_get_main_resource(test->m_webView);
+ g_assert(resource);
+ g_assert_cmpstr(webkit_web_resource_get_uri(resource), ==, javascriptURI.data());
+
+ CString simpleStyleCSSURI = kServer->getURIForPath("/simple-style-css.html");
+ test->loadURI(simpleStyleCSSURI.data());
+ test->waitUntilResourceLoadFinished();
+ resource = webkit_web_view_get_main_resource(test->m_webView);
+ g_assert(resource);
+ g_assert_cmpstr(webkit_web_resource_get_uri(resource), ==, simpleStyleCSSURI.data());
+
+ test->goBack();
+ test->waitUntilResourceLoadFinished();
+ resource = webkit_web_view_get_main_resource(test->m_webView);
+ g_assert(resource);
+ g_assert_cmpstr(webkit_web_resource_get_uri(resource), ==, javascriptURI.data());
+
+ test->goForward();
+ test->waitUntilResourceLoadFinished();
+ resource = webkit_web_view_get_main_resource(test->m_webView);
+ g_assert(resource);
+ g_assert_cmpstr(webkit_web_resource_get_uri(resource), ==, simpleStyleCSSURI.data());
+}
+
+class SendRequestTest: public SingleResourceLoadTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(SendRequestTest);
+
+ void resourceSentRequest(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse)
+ {
+ if (resource != m_resource)
+ return;
+
+ if (redirectResponse)
+ g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, m_expectedNewResourceURIAfterRedirection.data());
+ else
+ g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, m_expectedNewResourceURI.data());
+ g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, webkit_web_resource_get_uri(resource));
+
+ SingleResourceLoadTest::resourceSentRequest(resource, request, redirectResponse);
+ }
+
+ void resourceFailed(WebKitWebResource* resource, GError* error)
+ {
+ if (resource != m_resource)
+ return;
+
+ g_assert_cmpstr(webkit_web_resource_get_uri(resource), ==, m_expectedCancelledResourceURI.data());
+ g_assert_error(error, WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_CANCELLED);
+
+ SingleResourceLoadTest::resourceFailed(resource, error);
+ }
+
+ void setExpectedNewResourceURI(const CString& uri)
+ {
+ m_expectedNewResourceURI = uri;
+ }
+
+ void setExpectedCancelledResourceURI(const CString& uri)
+ {
+ m_expectedCancelledResourceURI = uri;
+ }
+
+ void setExpectedNewResourceURIAfterRedirection(const CString& uri)
+ {
+ m_expectedNewResourceURIAfterRedirection = uri;
+ }
+
+ CString m_expectedNewResourceURI;
+ CString m_expectedCancelledResourceURI;
+ CString m_expectedNewResourceURIAfterRedirection;
+};
+
+static void testWebResourceSendRequest(SendRequestTest* test, gconstpointer)
+{
+ test->setExpectedNewResourceURI(kServer->getURIForPath("/javascript.js"));
+ test->loadURI(kServer->getURIForPath("relative-javascript.html").data());
+ test->waitUntilResourceLoadFinished();
+ g_assert(test->m_resource);
+
+ Vector<SingleResourceLoadTest::LoadEvents>& events = test->m_loadEvents;
+ g_assert_cmpint(events.size(), ==, 5);
+ g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
+ g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
+ g_assert_cmpint(events[2], ==, SingleResourceLoadTest::ReceivedResponse);
+ g_assert_cmpint(events[3], ==, SingleResourceLoadTest::ReceivedData);
+ g_assert_cmpint(events[4], ==, SingleResourceLoadTest::Finished);
+ events.clear();
+
+ // Cancel request.
+ test->setExpectedCancelledResourceURI(kServer->getURIForPath("/cancel-this.js"));
+ test->loadURI(kServer->getURIForPath("/resource-to-cancel.html").data());
+ test->waitUntilResourceLoadFinished();
+ g_assert(test->m_resource);
+
+ g_assert_cmpint(events.size(), ==, 3);
+ g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
+ g_assert_cmpint(events[1], ==, SingleResourceLoadTest::Failed);
+ g_assert_cmpint(events[2], ==, SingleResourceLoadTest::Finished);
+ events.clear();
+
+ // URI changed after a redirect.
+ test->setExpectedNewResourceURI(kServer->getURIForPath("/redirected.js"));
+ test->setExpectedNewResourceURIAfterRedirection(kServer->getURIForPath("/javascript-after-redirection.js"));
+ test->loadURI(kServer->getURIForPath("redirected-javascript.html").data());
+ test->waitUntilResourceLoadFinished();
+ g_assert(test->m_resource);
+
+ g_assert_cmpint(events.size(), ==, 6);
+ g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
+ g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
+ g_assert_cmpint(events[2], ==, SingleResourceLoadTest::Redirected);
+ g_assert_cmpint(events[3], ==, SingleResourceLoadTest::ReceivedResponse);
+ g_assert_cmpint(events[4], ==, SingleResourceLoadTest::ReceivedData);
+ g_assert_cmpint(events[5], ==, SingleResourceLoadTest::Finished);
+ events.clear();
+
+ // Cancel after a redirect.
+ test->setExpectedNewResourceURI(kServer->getURIForPath("/redirected-to-cancel.js"));
+ test->setExpectedCancelledResourceURI(kServer->getURIForPath("/redirected-to-cancel.js"));
+ test->loadURI(kServer->getURIForPath("/redirected-to-cancel.html").data());
+ test->waitUntilResourceLoadFinished();
+ g_assert(test->m_resource);
+
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
+ g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
+ g_assert_cmpint(events[2], ==, SingleResourceLoadTest::Failed);
+ g_assert_cmpint(events[3], ==, SingleResourceLoadTest::Finished);
+ events.clear();
+}
+
+static GMutex s_serverMutex;
+static const unsigned s_maxConnectionsPerHost = 6;
+
+class SyncRequestOnMaxConnsTest: public ResourcesTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(SyncRequestOnMaxConnsTest);
+
+ void resourceLoadStarted(WebKitWebResource*, WebKitURIRequest*) override
+ {
+ if (!m_resourcesToStartPending)
+ return;
+
+ if (!--m_resourcesToStartPending)
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ void waitUntilResourcesStarted(unsigned requestCount)
+ {
+ m_resourcesToStartPending = requestCount;
+ g_main_loop_run(m_mainLoop);
+ }
+
+ unsigned m_resourcesToStartPending;
+};
+
+static void testWebViewSyncRequestOnMaxConns(SyncRequestOnMaxConnsTest* test, gconstpointer)
+{
+ WTF::GMutexLocker<GMutex> lock(s_serverMutex);
+ test->loadURI(kServer->getURIForPath("/sync-request-on-max-conns-0").data());
+ test->waitUntilResourcesStarted(s_maxConnectionsPerHost + 1); // s_maxConnectionsPerHost resource + main resource.
+
+ for (unsigned i = 0; i < 2; ++i) {
+ GUniquePtr<char> xhr(g_strdup_printf("xhr = new XMLHttpRequest; xhr.open('GET', '/sync-request-on-max-conns-xhr%u', false); xhr.send();", i));
+ webkit_web_view_run_javascript(test->m_webView, xhr.get(), nullptr, nullptr, nullptr);
+ }
+
+ // By default sync XHRs have a 10 seconds timeout, we don't want to wait all that so use our own timeout.
+ GMainLoopSource timeoutSource;
+ timeoutSource.scheduleAfterDelay("Timeout", [] { g_assert_not_reached(); }, std::chrono::seconds(1));
+
+ GMainLoopSource unlockServerSource;
+ unlockServerSource.schedule("Unlock Server Idle", [&lock] { lock.unlock(); });
+ test->waitUntilResourcesLoaded(s_maxConnectionsPerHost + 3); // s_maxConnectionsPerHost resource + main resource + 2 XHR.
+}
+
+static void addCacheHTTPHeadersToResponse(SoupMessage* message)
+{
+ // The actual date doesn't really matter.
+ SoupDate* soupDate = soup_date_new_from_now(0);
+ GUniquePtr<char> date(soup_date_to_string(soupDate, SOUP_DATE_HTTP));
+ soup_message_headers_append(message->response_headers, "Last-Modified", date.get());
+ soup_date_free(soupDate);
+ soup_message_headers_append(message->response_headers, "Cache-control", "public, max-age=31536000");
+ soupDate = soup_date_new_from_now(3600);
+ date.reset(soup_date_to_string(soupDate, SOUP_DATE_HTTP));
+ soup_message_headers_append(message->response_headers, "Expires", date.get());
+ soup_date_free(soupDate);
+}
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ soup_message_set_status(message, SOUP_STATUS_OK);
+
+ if (soup_message_headers_get_one(message->request_headers, "If-Modified-Since")) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_MODIFIED);
+ soup_message_body_complete(message->response_body);
+ return;
+ }
+
+ if (g_str_equal(path, "/")) {
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kIndexHtml, strlen(kIndexHtml));
+ } else if (g_str_equal(path, "/javascript.html")) {
+ static const char* javascriptHtml = "<html><head><script language='javascript' src='/javascript.js'></script></head><body></body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, javascriptHtml, strlen(javascriptHtml));
+ } else if (g_str_equal(path, "/image.html")) {
+ static const char* imageHTML = "<html><body><img src='/blank.ico'></img></body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, imageHTML, strlen(imageHTML));
+ } else if (g_str_equal(path, "/redirected-css.html")) {
+ static const char* redirectedCSSHtml = "<html><head><link rel='stylesheet' href='/redirected.css' type='text/css'></head><body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, redirectedCSSHtml, strlen(redirectedCSSHtml));
+ } else if (g_str_equal(path, "/invalid-css.html")) {
+ static const char* invalidCSSHtml = "<html><head><link rel='stylesheet' href='/invalid.css' type='text/css'></head><body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, invalidCSSHtml, strlen(invalidCSSHtml));
+ } else if (g_str_equal(path, "/simple-style-css.html")) {
+ static const char* simpleStyleCSSHtml = "<html><head><link rel='stylesheet' href='/simple-style.css' type='text/css'></head><body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, simpleStyleCSSHtml, strlen(simpleStyleCSSHtml));
+ } else if (g_str_equal(path, "/style.css")) {
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kStyleCSS, strlen(kStyleCSS));
+ addCacheHTTPHeadersToResponse(message);
+ soup_message_headers_append(message->response_headers, "Content-Type", "text/css");
+ } else if (g_str_equal(path, "/javascript.js") || g_str_equal(path, "/javascript-after-redirection.js")) {
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kJavascript, strlen(kJavascript));
+ soup_message_headers_append(message->response_headers, "Content-Type", "text/javascript");
+ soup_message_headers_append(message->response_headers, "Content-Disposition", "filename=JavaScript.js");
+ } else if (g_str_equal(path, "/relative-javascript.html")) {
+ static const char* javascriptRelativeHTML = "<html><head><script language='javascript' src='remove-this/javascript.js'></script></head><body></body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, javascriptRelativeHTML, strlen(javascriptRelativeHTML));
+ } else if (g_str_equal(path, "/resource-to-cancel.html")) {
+ static const char* resourceToCancelHTML = "<html><head><script language='javascript' src='cancel-this.js'></script></head><body></body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, resourceToCancelHTML, strlen(resourceToCancelHTML));
+ } else if (g_str_equal(path, "/redirected-javascript.html")) {
+ static const char* javascriptRelativeHTML = "<html><head><script language='javascript' src='/redirected.js'></script></head><body></body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, javascriptRelativeHTML, strlen(javascriptRelativeHTML));
+ } else if (g_str_equal(path, "/redirected-to-cancel.html")) {
+ static const char* javascriptRelativeHTML = "<html><head><script language='javascript' src='/redirected-to-cancel.js'></script></head><body></body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, javascriptRelativeHTML, strlen(javascriptRelativeHTML));
+ } else if (g_str_equal(path, "/blank.ico")) {
+ GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), path, nullptr));
+ char* contents;
+ gsize contentsLength;
+ g_file_get_contents(filePath.get(), &contents, &contentsLength, 0);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, contentsLength);
+ addCacheHTTPHeadersToResponse(message);
+ soup_message_headers_append(message->response_headers, "Content-Type", "image/vnd.microsoft.icon");
+ } else if (g_str_equal(path, "/simple-style.css")) {
+ static const char* simpleCSS =
+ "body {"
+ " margin: 0px;"
+ " padding: 0px;"
+ "}";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, simpleCSS, strlen(simpleCSS));
+ soup_message_headers_append(message->response_headers, "Content-Type", "text/css");
+ } else if (g_str_equal(path, "/redirected.css")) {
+ soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
+ soup_message_headers_append(message->response_headers, "Location", "/simple-style.css");
+ } else if (g_str_equal(path, "/redirected.js")) {
+ soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
+ soup_message_headers_append(message->response_headers, "Location", "/remove-this/javascript-after-redirection.js");
+ } else if (g_str_equal(path, "/redirected-to-cancel.js")) {
+ soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
+ soup_message_headers_append(message->response_headers, "Location", "/cancel-this.js");
+ } else if (g_str_equal(path, "/invalid.css"))
+ soup_message_set_status(message, SOUP_STATUS_CANT_CONNECT);
+ else if (g_str_has_prefix(path, "/sync-request-on-max-conns-")) {
+ char* contents;
+ gsize contentsLength;
+ if (g_str_equal(path, "/sync-request-on-max-conns-0")) {
+ GString* imagesHTML = g_string_new("<html><body>");
+ for (unsigned i = 1; i <= s_maxConnectionsPerHost; ++i)
+ g_string_append_printf(imagesHTML, "<img src='/sync-request-on-max-conns-%u'>", i);
+ g_string_append(imagesHTML, "</body></html>");
+
+ contentsLength = imagesHTML->len;
+ contents = g_string_free(imagesHTML, FALSE);
+ } else {
+ {
+ // We don't actually need to keep the mutex, so we release it as soon as we get it.
+ WTF::GMutexLocker<GMutex> lock(s_serverMutex);
+ }
+
+ GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr));
+ g_file_get_contents(filePath.get(), &contents, &contentsLength, 0);
+ }
+ soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, contentsLength);
+ } else
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+ soup_message_body_complete(message->response_body);
+}
+
+void beforeAll()
+{
+ kServer = new WebKitTestServer();
+ kServer->run(serverCallback);
+
+ ResourcesTest::add("WebKitWebView", "resources", testWebViewResources);
+ SingleResourceLoadTest::add("WebKitWebResource", "loading", testWebResourceLoading);
+ SingleResourceLoadTest::add("WebKitWebResource", "response", testWebResourceResponse);
+ SingleResourceLoadTest::add("WebKitWebResource", "mime-type", testWebResourceMimeType);
+ SingleResourceLoadTest::add("WebKitWebResource", "suggested-filename", testWebResourceSuggestedFilename);
+ ResourceURITrackingTest::add("WebKitWebResource", "active-uri", testWebResourceActiveURI);
+ ResourcesTest::add("WebKitWebResource", "get-data", testWebResourceGetData);
+ SingleResourceLoadTest::add("WebKitWebView", "history-cache", testWebViewResourcesHistoryCache);
+ SendRequestTest::add("WebKitWebPage", "send-request", testWebResourceSendRequest);
+#if SOUP_CHECK_VERSION(2, 49, 91)
+ SyncRequestOnMaxConnsTest::add("WebKitWebView", "sync-request-on-max-conns", testWebViewSyncRequestOnMaxConns);
+#endif
+}
+
+void afterAll()
+{
+ delete kServer;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestSSL.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestSSL.cpp
new file mode 100644
index 000000000..10c5e73b6
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestSSL.cpp
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "LoadTrackingTest.h"
+#include "WebKitTestServer.h"
+#include <gtk/gtk.h>
+
+static WebKitTestServer* kHttpsServer;
+static WebKitTestServer* kHttpServer;
+
+static const char* indexHTML = "<html><body>Testing WebKit2GTK+ SSL</body></htmll>";
+static const char* insecureContentHTML = "<html><script src=\"%s\"></script><body><p>Text + image <img src=\"%s\" align=\"right\"/></p></body></html>";
+static const char TLSExpectedSuccessTitle[] = "WebKit2Gtk+ TLS permission test";
+static const char TLSSuccessHTMLString[] = "<html><head><title>WebKit2Gtk+ TLS permission test</title></head><body></body></html>";
+
+class SSLTest: public LoadTrackingTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(SSLTest);
+
+ SSLTest()
+ : m_tlsErrors(static_cast<GTlsCertificateFlags>(0))
+ {
+ }
+
+ virtual void provisionalLoadFailed(const gchar* failingURI, GError* error)
+ {
+ g_assert_error(error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED);
+ LoadTrackingTest::provisionalLoadFailed(failingURI, error);
+ }
+
+ virtual void loadCommitted()
+ {
+ GTlsCertificate* certificate = 0;
+ webkit_web_view_get_tls_info(m_webView, &certificate, &m_tlsErrors);
+ m_certificate = certificate;
+ LoadTrackingTest::loadCommitted();
+ }
+
+ void waitUntilLoadFinished()
+ {
+ m_certificate = 0;
+ m_tlsErrors = static_cast<GTlsCertificateFlags>(0);
+ LoadTrackingTest::waitUntilLoadFinished();
+ }
+
+ GRefPtr<GTlsCertificate> m_certificate;
+ GTlsCertificateFlags m_tlsErrors;
+};
+
+static void testSSL(SSLTest* test, gconstpointer)
+{
+ WebKitWebContext* context = webkit_web_view_get_context(test->m_webView);
+ WebKitTLSErrorsPolicy originalPolicy = webkit_web_context_get_tls_errors_policy(context);
+ webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+
+ test->loadURI(kHttpsServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+ g_assert(test->m_certificate);
+ // We always expect errors because we are using a self-signed certificate,
+ // but only G_TLS_CERTIFICATE_UNKNOWN_CA flags should be present.
+ g_assert(test->m_tlsErrors);
+ g_assert_cmpuint(test->m_tlsErrors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
+
+ // Non HTTPS loads shouldn't have a certificate nor errors.
+ test->loadHtml(indexHTML, 0);
+ test->waitUntilLoadFinished();
+ g_assert(!test->m_certificate);
+ g_assert(!test->m_tlsErrors);
+
+ webkit_web_context_set_tls_errors_policy(context, originalPolicy);
+}
+
+class InsecureContentTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(InsecureContentTest);
+
+ InsecureContentTest()
+ : m_insecureContentRun(false)
+ , m_insecureContentDisplayed(false)
+ {
+ g_signal_connect(m_webView, "insecure-content-detected", G_CALLBACK(insecureContentDetectedCallback), this);
+ }
+
+ static void insecureContentDetectedCallback(WebKitWebView* webView, WebKitInsecureContentEvent event, InsecureContentTest* test)
+ {
+ g_assert(webView == test->m_webView);
+
+ if (event == WEBKIT_INSECURE_CONTENT_RUN)
+ test->m_insecureContentRun = true;
+
+ if (event == WEBKIT_INSECURE_CONTENT_DISPLAYED)
+ test->m_insecureContentDisplayed = true;
+ }
+
+ bool m_insecureContentRun;
+ bool m_insecureContentDisplayed;
+};
+
+static void testInsecureContent(InsecureContentTest* test, gconstpointer)
+{
+ WebKitWebContext* context = webkit_web_view_get_context(test->m_webView);
+ WebKitTLSErrorsPolicy originalPolicy = webkit_web_context_get_tls_errors_policy(context);
+ webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+
+ test->loadURI(kHttpsServer->getURIForPath("/insecure-content/").data());
+ test->waitUntilLoadFinished();
+
+ g_assert(!test->m_insecureContentRun);
+ // Images are currently always displayed, even bypassing mixed content settings. Check
+ // https://bugs.webkit.org/show_bug.cgi?id=142469
+ g_assert(test->m_insecureContentDisplayed);
+
+ webkit_web_context_set_tls_errors_policy(context, originalPolicy);
+}
+
+static bool assertIfSSLRequestProcessed = false;
+
+static void testTLSErrorsPolicy(SSLTest* test, gconstpointer)
+{
+ WebKitWebContext* context = webkit_web_view_get_context(test->m_webView);
+ // TLS errors are treated as transport failures by default.
+ g_assert(webkit_web_context_get_tls_errors_policy(context) == WEBKIT_TLS_ERRORS_POLICY_FAIL);
+
+ assertIfSSLRequestProcessed = true;
+ test->loadURI(kHttpsServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+ g_assert(test->m_loadFailed);
+ g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
+ g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadCommitted));
+ assertIfSSLRequestProcessed = false;
+
+ webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+ g_assert(webkit_web_context_get_tls_errors_policy(context) == WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+
+ test->m_loadFailed = false;
+ test->loadURI(kHttpsServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+ g_assert(!test->m_loadFailed);
+
+ webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL);
+ g_assert(webkit_web_context_get_tls_errors_policy(context) == WEBKIT_TLS_ERRORS_POLICY_FAIL);
+}
+
+static void testTLSErrorsRedirect(SSLTest* test, gconstpointer)
+{
+ WebKitWebContext* context = webkit_web_view_get_context(test->m_webView);
+ WebKitTLSErrorsPolicy originalPolicy = webkit_web_context_get_tls_errors_policy(context);
+ webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL);
+
+ assertIfSSLRequestProcessed = true;
+ test->loadURI(kHttpsServer->getURIForPath("/redirect").data());
+ test->waitUntilLoadFinished();
+ g_assert(test->m_loadFailed);
+ g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
+ g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadCommitted));
+ assertIfSSLRequestProcessed = false;
+
+ webkit_web_context_set_tls_errors_policy(context, originalPolicy);
+}
+
+static gboolean webViewAuthenticationCallback(WebKitWebView*, WebKitAuthenticationRequest* request)
+{
+ g_assert_not_reached();
+ return TRUE;
+}
+
+
+static void testTLSErrorsHTTPAuth(SSLTest* test, gconstpointer)
+{
+ WebKitWebContext* context = webkit_web_view_get_context(test->m_webView);
+ WebKitTLSErrorsPolicy originalPolicy = webkit_web_context_get_tls_errors_policy(context);
+ webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL);
+
+ assertIfSSLRequestProcessed = true;
+ g_signal_connect(test->m_webView, "authenticate", G_CALLBACK(webViewAuthenticationCallback), NULL);
+ test->loadURI(kHttpsServer->getURIForPath("/auth").data());
+ test->waitUntilLoadFinished();
+ g_assert(test->m_loadFailed);
+ g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
+ g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadCommitted));
+ assertIfSSLRequestProcessed = false;
+
+ webkit_web_context_set_tls_errors_policy(context, originalPolicy);
+}
+
+class TLSErrorsTest: public SSLTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(TLSErrorsTest);
+
+ TLSErrorsTest()
+ : m_tlsErrors(static_cast<GTlsCertificateFlags>(0))
+ , m_failingURI(nullptr)
+ {
+ }
+
+ ~TLSErrorsTest()
+ {
+ if (m_failingURI)
+ soup_uri_free(m_failingURI);
+ }
+
+ bool loadFailedWithTLSErrors(const char* failingURI, GTlsCertificate* certificate, GTlsCertificateFlags tlsErrors) override
+ {
+ LoadTrackingTest::loadFailedWithTLSErrors(failingURI, certificate, tlsErrors);
+
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(certificate));
+ m_certificate = certificate;
+ m_tlsErrors = tlsErrors;
+ if (m_failingURI)
+ soup_uri_free(m_failingURI);
+ m_failingURI = soup_uri_new(failingURI);
+ return true;
+ }
+
+ GTlsCertificate* certificate() const { return m_certificate.get(); }
+ GTlsCertificateFlags tlsErrors() const { return m_tlsErrors; }
+ const char* host() const { return m_failingURI->host; }
+
+private:
+ GRefPtr<GTlsCertificate> m_certificate;
+ GTlsCertificateFlags m_tlsErrors;
+ SoupURI* m_failingURI;
+};
+
+static void testLoadFailedWithTLSErrors(TLSErrorsTest* test, gconstpointer)
+{
+ WebKitWebContext* context = webkit_web_view_get_context(test->m_webView);
+ WebKitTLSErrorsPolicy originalPolicy = webkit_web_context_get_tls_errors_policy(context);
+ webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL);
+
+ assertIfSSLRequestProcessed = true;
+ // The load-failed-with-tls-errors signal should be emitted when there is a TLS failure.
+ test->loadURI(kHttpsServer->getURIForPath("/test-tls/").data());
+ test->waitUntilLoadFinished();
+ g_assert(G_IS_TLS_CERTIFICATE(test->certificate()));
+ g_assert_cmpuint(test->tlsErrors(), ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
+ g_assert_cmpstr(test->host(), ==, soup_uri_get_host(kHttpsServer->baseURI()));
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadFailedWithTLSErrors);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+ assertIfSSLRequestProcessed = false;
+
+ // Test allowing an exception for this certificate on this host.
+ webkit_web_context_allow_tls_certificate_for_host(context, test->certificate(), test->host());
+ // The page should now load without errors.
+ test->loadURI(kHttpsServer->getURIForPath("/test-tls/").data());
+ test->waitUntilLoadFinished();
+
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+ g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, TLSExpectedSuccessTitle);
+
+ webkit_web_context_set_tls_errors_policy(context, originalPolicy);
+}
+
+class TLSSubresourceTest : public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(TLSSubresourceTest);
+
+ static void resourceLoadStartedCallback(WebKitWebView* webView, WebKitWebResource* resource, WebKitURIRequest* request, TLSSubresourceTest* test)
+ {
+ if (webkit_web_view_get_main_resource(test->m_webView) == resource)
+ return;
+
+ // Ignore favicons.
+ if (g_str_has_suffix(webkit_uri_request_get_uri(request), "favicon.ico"))
+ return;
+
+ test->subresourceLoadStarted(resource);
+ }
+
+ TLSSubresourceTest()
+ : m_tlsErrors(static_cast<GTlsCertificateFlags>(0))
+ {
+ g_signal_connect(m_webView, "resource-load-started", G_CALLBACK(resourceLoadStartedCallback), this);
+ }
+
+ static void subresourceFailedCallback(WebKitWebResource*, GError*)
+ {
+ g_assert_not_reached();
+ }
+
+ static void subresourceFailedWithTLSErrorsCallback(WebKitWebResource* resource, GTlsCertificate* certificate, GTlsCertificateFlags tlsErrors, TLSSubresourceTest* test)
+ {
+ test->subresourceFailedWithTLSErrors(resource, certificate, tlsErrors);
+ }
+
+ void subresourceLoadStarted(WebKitWebResource* resource)
+ {
+ g_signal_connect(resource, "failed", G_CALLBACK(subresourceFailedCallback), nullptr);
+ g_signal_connect(resource, "failed-with-tls-errors", G_CALLBACK(subresourceFailedWithTLSErrorsCallback), this);
+ }
+
+ void subresourceFailedWithTLSErrors(WebKitWebResource* resource, GTlsCertificate* certificate, GTlsCertificateFlags tlsErrors)
+ {
+ m_certificate = certificate;
+ m_tlsErrors = tlsErrors;
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ void waitUntilSubresourceLoadFail()
+ {
+ g_main_loop_run(m_mainLoop);
+ }
+
+ GRefPtr<GTlsCertificate> m_certificate;
+ GTlsCertificateFlags m_tlsErrors;
+};
+
+static void testSubresourceLoadFailedWithTLSErrors(TLSSubresourceTest* test, gconstpointer)
+{
+ WebKitWebContext* context = webkit_web_view_get_context(test->m_webView);
+ webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL);
+
+ assertIfSSLRequestProcessed = true;
+ test->loadURI(kHttpServer->getURIForPath("/").data());
+ test->waitUntilSubresourceLoadFail();
+ g_assert(G_IS_TLS_CERTIFICATE(test->m_certificate.get()));
+ g_assert_cmpuint(test->m_tlsErrors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
+ assertIfSSLRequestProcessed = false;
+}
+
+static void httpsServerCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ g_assert(!assertIfSSLRequestProcessed);
+
+ if (g_str_equal(path, "/")) {
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, indexHTML, strlen(indexHTML));
+ soup_message_body_complete(message->response_body);
+ } else if (g_str_equal(path, "/insecure-content/")) {
+ GUniquePtr<char> responseHTML(g_strdup_printf(insecureContentHTML, kHttpServer->getURIForPath("/test-script").data(), kHttpServer->getURIForPath("/test-image").data()));
+ soup_message_body_append(message->response_body, SOUP_MEMORY_COPY, responseHTML.get(), strlen(responseHTML.get()));
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_complete(message->response_body);
+ } else if (g_str_equal(path, "/test-tls/")) {
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, TLSSuccessHTMLString, strlen(TLSSuccessHTMLString));
+ soup_message_body_complete(message->response_body);
+ } else if (g_str_equal(path, "/redirect")) {
+ soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
+ soup_message_headers_append(message->response_headers, "Location", kHttpServer->getURIForPath("/test-image").data());
+ } else if (g_str_equal(path, "/auth")) {
+ soup_message_set_status(message, SOUP_STATUS_UNAUTHORIZED);
+ soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic realm=\"HTTPS auth\"");
+ } else if (g_str_equal(path, "/style.css")) {
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ static const char* styleCSS = "body { color: black; }";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, styleCSS, strlen(styleCSS));
+ } else
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+}
+
+static void httpServerCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (g_str_equal(path, "/test-script")) {
+ GUniquePtr<char> pathToFile(g_build_filename(Test::getResourcesDir().data(), "link-title.js", nullptr));
+ char* contents;
+ gsize length;
+ g_file_get_contents(pathToFile.get(), &contents, &length, 0);
+
+ soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, length);
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_complete(message->response_body);
+ } else if (g_str_equal(path, "/test-image")) {
+ GUniquePtr<char> pathToFile(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr));
+ char* contents;
+ gsize length;
+ g_file_get_contents(pathToFile.get(), &contents, &length, 0);
+
+ soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, length);
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_complete(message->response_body);
+ } else if (g_str_equal(path, "/")) {
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ char* responseHTML = g_strdup_printf("<html><head><link rel='stylesheet' href='%s' type='text/css'></head><body>SSL subresource test</body></html>",
+ kHttpsServer->getURIForPath("/style.css").data());
+ soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, responseHTML, strlen(responseHTML));
+ soup_message_body_complete(message->response_body);
+ } else
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+}
+
+void beforeAll()
+{
+ kHttpsServer = new WebKitTestServer(WebKitTestServer::ServerHTTPS);
+ kHttpsServer->run(httpsServerCallback);
+
+ kHttpServer = new WebKitTestServer(WebKitTestServer::ServerHTTP);
+ kHttpServer->run(httpServerCallback);
+
+ SSLTest::add("WebKitWebView", "ssl", testSSL);
+ InsecureContentTest::add("WebKitWebView", "insecure-content", testInsecureContent);
+ SSLTest::add("WebKitWebView", "tls-errors-policy", testTLSErrorsPolicy);
+ SSLTest::add("WebKitWebView", "tls-errors-redirect-to-http", testTLSErrorsRedirect);
+ SSLTest::add("WebKitWebView", "tls-http-auth", testTLSErrorsHTTPAuth);
+ TLSSubresourceTest::add("WebKitWebView", "tls-subresource", testSubresourceLoadFailedWithTLSErrors);
+ TLSErrorsTest::add("WebKitWebView", "load-failed-with-tls-errors", testLoadFailedWithTLSErrors);
+}
+
+void afterAll()
+{
+ delete kHttpsServer;
+ delete kHttpServer;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp
new file mode 100644
index 000000000..ae89be18c
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp
@@ -0,0 +1,984 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebViewTest.h"
+#include <wtf/HashSet.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/text/StringHash.h>
+
+static const char* kAlertDialogMessage = "WebKitGTK+ alert dialog message";
+static const char* kConfirmDialogMessage = "WebKitGTK+ confirm dialog message";
+static const char* kPromptDialogMessage = "WebKitGTK+ prompt dialog message";
+static const char* kPromptDialogReturnedText = "WebKitGTK+ prompt dialog returned text";
+
+class UIClientTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(UIClientTest);
+
+ enum WebViewEvents {
+ Create,
+ ReadyToShow,
+ RunAsModal,
+ Close
+ };
+
+ class WindowProperties {
+ public:
+ WindowProperties()
+ : m_isNull(true)
+ , m_toolbarVisible(true)
+ , m_statusbarVisible(true)
+ , m_scrollbarsVisible(true)
+ , m_menubarVisible(true)
+ , m_locationbarVisible(true)
+ , m_resizable(true)
+ , m_fullscreen(false)
+ {
+ memset(&m_geometry, 0, sizeof(GdkRectangle));
+ }
+
+ WindowProperties(WebKitWindowProperties* windowProperties)
+ : m_isNull(false)
+ , m_toolbarVisible(webkit_window_properties_get_toolbar_visible(windowProperties))
+ , m_statusbarVisible(webkit_window_properties_get_statusbar_visible(windowProperties))
+ , m_scrollbarsVisible(webkit_window_properties_get_scrollbars_visible(windowProperties))
+ , m_menubarVisible(webkit_window_properties_get_menubar_visible(windowProperties))
+ , m_locationbarVisible(webkit_window_properties_get_locationbar_visible(windowProperties))
+ , m_resizable(webkit_window_properties_get_resizable(windowProperties))
+ , m_fullscreen(webkit_window_properties_get_fullscreen(windowProperties))
+ {
+ webkit_window_properties_get_geometry(windowProperties, &m_geometry);
+ }
+
+ WindowProperties(GdkRectangle* geometry, bool toolbarVisible, bool statusbarVisible, bool scrollbarsVisible, bool menubarVisible, bool locationbarVisible, bool resizable, bool fullscreen)
+ : m_isNull(false)
+ , m_geometry(*geometry)
+ , m_toolbarVisible(toolbarVisible)
+ , m_statusbarVisible(statusbarVisible)
+ , m_scrollbarsVisible(scrollbarsVisible)
+ , m_menubarVisible(menubarVisible)
+ , m_locationbarVisible(locationbarVisible)
+ , m_resizable(resizable)
+ , m_fullscreen(fullscreen)
+ {
+ }
+
+ bool isNull() const { return m_isNull; }
+
+ void assertEqual(const WindowProperties& other) const
+ {
+ g_assert_cmpint(m_geometry.x, ==, other.m_geometry.x);
+ g_assert_cmpint(m_geometry.y, ==, other.m_geometry.y);
+ g_assert_cmpint(m_geometry.width, ==, other.m_geometry.width);
+ g_assert_cmpint(m_geometry.height, ==, other.m_geometry.height);
+ g_assert_cmpint(static_cast<int>(m_toolbarVisible), ==, static_cast<int>(other.m_toolbarVisible));
+ g_assert_cmpint(static_cast<int>(m_statusbarVisible), ==, static_cast<int>(other.m_statusbarVisible));
+ g_assert_cmpint(static_cast<int>(m_scrollbarsVisible), ==, static_cast<int>(other.m_scrollbarsVisible));
+ g_assert_cmpint(static_cast<int>(m_menubarVisible), ==, static_cast<int>(other.m_menubarVisible));
+ g_assert_cmpint(static_cast<int>(m_locationbarVisible), ==, static_cast<int>(other.m_locationbarVisible));
+ g_assert_cmpint(static_cast<int>(m_resizable), ==, static_cast<int>(other.m_resizable));
+ g_assert_cmpint(static_cast<int>(m_fullscreen), ==, static_cast<int>(other.m_fullscreen));
+ }
+
+ private:
+ bool m_isNull;
+
+ GdkRectangle m_geometry;
+
+ bool m_toolbarVisible;
+ bool m_statusbarVisible;
+ bool m_scrollbarsVisible;
+ bool m_menubarVisible;
+ bool m_locationbarVisible;
+
+ bool m_resizable;
+ bool m_fullscreen;
+ };
+
+ static void windowPropertiesNotifyCallback(GObject*, GParamSpec* paramSpec, UIClientTest* test)
+ {
+ test->m_windowPropertiesChanged.add(g_param_spec_get_name(paramSpec));
+ }
+
+ static GtkWidget* viewCreateCallback(WebKitWebView* webView, WebKitNavigationAction* navigation, UIClientTest* test)
+ {
+ return test->viewCreate(webView, navigation);
+ }
+
+ static void viewReadyToShowCallback(WebKitWebView* webView, UIClientTest* test)
+ {
+ test->viewReadyToShow(webView);
+ }
+
+ static void viewCloseCallback(WebKitWebView* webView, UIClientTest* test)
+ {
+ test->viewClose(webView);
+ }
+
+ void scriptAlert(WebKitScriptDialog* dialog)
+ {
+ switch (m_scriptDialogType) {
+ case WEBKIT_SCRIPT_DIALOG_ALERT:
+ g_assert_cmpstr(webkit_script_dialog_get_message(dialog), ==, kAlertDialogMessage);
+ break;
+ case WEBKIT_SCRIPT_DIALOG_CONFIRM:
+ g_assert(m_scriptDialogConfirmed);
+ g_assert_cmpstr(webkit_script_dialog_get_message(dialog), ==, "confirmed");
+
+ break;
+ case WEBKIT_SCRIPT_DIALOG_PROMPT:
+ g_assert_cmpstr(webkit_script_dialog_get_message(dialog), ==, kPromptDialogReturnedText);
+ break;
+ }
+
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ void scriptConfirm(WebKitScriptDialog* dialog)
+ {
+ g_assert_cmpstr(webkit_script_dialog_get_message(dialog), ==, kConfirmDialogMessage);
+ m_scriptDialogConfirmed = !m_scriptDialogConfirmed;
+ webkit_script_dialog_confirm_set_confirmed(dialog, m_scriptDialogConfirmed);
+ }
+
+ void scriptPrompt(WebKitScriptDialog* dialog)
+ {
+ g_assert_cmpstr(webkit_script_dialog_get_message(dialog), ==, kPromptDialogMessage);
+ g_assert_cmpstr(webkit_script_dialog_prompt_get_default_text(dialog), ==, "default");
+ webkit_script_dialog_prompt_set_text(dialog, kPromptDialogReturnedText);
+ }
+
+ static gboolean scriptDialog(WebKitWebView*, WebKitScriptDialog* dialog, UIClientTest* test)
+ {
+ switch (webkit_script_dialog_get_dialog_type(dialog)) {
+ case WEBKIT_SCRIPT_DIALOG_ALERT:
+ test->scriptAlert(dialog);
+ break;
+ case WEBKIT_SCRIPT_DIALOG_CONFIRM:
+ test->scriptConfirm(dialog);
+ break;
+ case WEBKIT_SCRIPT_DIALOG_PROMPT:
+ test->scriptPrompt(dialog);
+ break;
+ }
+
+ return TRUE;
+ }
+
+ static void mouseTargetChanged(WebKitWebView*, WebKitHitTestResult* hitTestResult, guint modifiers, UIClientTest* test)
+ {
+ g_assert(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(hitTestResult));
+
+ test->m_mouseTargetHitTestResult = hitTestResult;
+ test->m_mouseTargetModifiers = modifiers;
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ static gboolean permissionRequested(WebKitWebView*, WebKitPermissionRequest* request, UIClientTest* test)
+ {
+ g_assert(WEBKIT_IS_PERMISSION_REQUEST(request));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
+
+ if (test->m_verifyMediaTypes && WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(request)) {
+ WebKitUserMediaPermissionRequest* userMediaRequest = WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request);
+ g_assert(webkit_user_media_permission_is_for_audio_device(userMediaRequest) == test->m_expectedAudioMedia);
+ g_assert(webkit_user_media_permission_is_for_video_device(userMediaRequest) == test->m_expectedVideoMedia);
+ }
+
+ if (test->m_allowPermissionRequests)
+ webkit_permission_request_allow(request);
+ else
+ webkit_permission_request_deny(request);
+
+ return TRUE;
+ }
+
+ UIClientTest()
+ : m_scriptDialogType(WEBKIT_SCRIPT_DIALOG_ALERT)
+ , m_scriptDialogConfirmed(true)
+ , m_allowPermissionRequests(false)
+ , m_verifyMediaTypes(false)
+ , m_expectedAudioMedia(false)
+ , m_expectedVideoMedia(false)
+ , m_mouseTargetModifiers(0)
+ {
+ webkit_settings_set_javascript_can_open_windows_automatically(webkit_web_view_get_settings(m_webView), TRUE);
+ g_signal_connect(m_webView, "create", G_CALLBACK(viewCreateCallback), this);
+ g_signal_connect(m_webView, "script-dialog", G_CALLBACK(scriptDialog), this);
+ g_signal_connect(m_webView, "mouse-target-changed", G_CALLBACK(mouseTargetChanged), this);
+ g_signal_connect(m_webView, "permission-request", G_CALLBACK(permissionRequested), this);
+ }
+
+ ~UIClientTest()
+ {
+ g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ void waitUntilMainLoopFinishes()
+ {
+ g_main_loop_run(m_mainLoop);
+ }
+
+ void setExpectedWindowProperties(const WindowProperties& windowProperties)
+ {
+ m_windowProperties = windowProperties;
+ }
+
+ WebKitHitTestResult* moveMouseAndWaitUntilMouseTargetChanged(int x, int y, unsigned mouseModifiers = 0)
+ {
+ mouseMoveTo(x, y, mouseModifiers);
+ g_main_loop_run(m_mainLoop);
+ return m_mouseTargetHitTestResult.get();
+ }
+
+ virtual GtkWidget* viewCreate(WebKitWebView* webView, WebKitNavigationAction* navigation)
+ {
+ g_assert(webView == m_webView);
+ g_assert(navigation);
+
+ GtkWidget* newWebView = webkit_web_view_new_with_context(webkit_web_view_get_context(webView));
+ g_object_ref_sink(newWebView);
+
+ m_webViewEvents.append(Create);
+
+ WebKitWindowProperties* windowProperties = webkit_web_view_get_window_properties(WEBKIT_WEB_VIEW(newWebView));
+ g_assert(windowProperties);
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(windowProperties));
+ m_windowPropertiesChanged.clear();
+
+ g_signal_connect(windowProperties, "notify", G_CALLBACK(windowPropertiesNotifyCallback), this);
+ g_signal_connect(newWebView, "ready-to-show", G_CALLBACK(viewReadyToShowCallback), this);
+ g_signal_connect(newWebView, "close", G_CALLBACK(viewCloseCallback), this);
+
+ return newWebView;
+ }
+
+ virtual void viewReadyToShow(WebKitWebView* webView)
+ {
+ g_assert(webView != m_webView);
+
+ WebKitWindowProperties* windowProperties = webkit_web_view_get_window_properties(webView);
+ g_assert(windowProperties);
+ if (!m_windowProperties.isNull())
+ WindowProperties(windowProperties).assertEqual(m_windowProperties);
+
+ m_webViewEvents.append(ReadyToShow);
+ }
+
+ virtual void viewClose(WebKitWebView* webView)
+ {
+ g_assert(webView != m_webView);
+
+ m_webViewEvents.append(Close);
+ g_object_unref(webView);
+
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ Vector<WebViewEvents> m_webViewEvents;
+ WebKitScriptDialogType m_scriptDialogType;
+ bool m_scriptDialogConfirmed;
+ bool m_allowPermissionRequests;
+ gboolean m_verifyMediaTypes;
+ gboolean m_expectedAudioMedia;
+ gboolean m_expectedVideoMedia;
+ WindowProperties m_windowProperties;
+ HashSet<WTF::String> m_windowPropertiesChanged;
+ GRefPtr<WebKitHitTestResult> m_mouseTargetHitTestResult;
+ unsigned m_mouseTargetModifiers;
+};
+
+static void testWebViewCreateReadyClose(UIClientTest* test, gconstpointer)
+{
+ test->loadHtml("<html><body onLoad=\"window.open().close();\"></html>", 0);
+ test->waitUntilMainLoopFinishes();
+
+ Vector<UIClientTest::WebViewEvents>& events = test->m_webViewEvents;
+ g_assert_cmpint(events.size(), ==, 3);
+ g_assert_cmpint(events[0], ==, UIClientTest::Create);
+ g_assert_cmpint(events[1], ==, UIClientTest::ReadyToShow);
+ g_assert_cmpint(events[2], ==, UIClientTest::Close);
+}
+
+class CreateNavigationDataTest: public UIClientTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(CreateNavigationDataTest);
+
+ CreateNavigationDataTest()
+ : m_navigation(nullptr)
+ {
+ }
+
+ ~CreateNavigationDataTest()
+ {
+ clearNavigation();
+ }
+
+ void clearNavigation()
+ {
+ if (m_navigation)
+ webkit_navigation_action_free(m_navigation);
+ m_navigation = nullptr;
+ }
+
+ GtkWidget* viewCreate(WebKitWebView* webView, WebKitNavigationAction* navigation)
+ {
+ g_assert(navigation);
+ g_assert(!m_navigation);
+ m_navigation = webkit_navigation_action_copy(navigation);
+ g_main_loop_quit(m_mainLoop);
+ return nullptr;
+ }
+
+ void loadHTML(const char* html)
+ {
+ clearNavigation();
+ WebViewTest::loadHtml(html, nullptr);
+ }
+
+ void clickAndWaitUntilMainLoopFinishes(int x, int y)
+ {
+ clearNavigation();
+ clickMouseButton(x, y, 1);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ WebKitNavigationAction* m_navigation;
+};
+
+static void testWebViewCreateNavigationData(CreateNavigationDataTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+
+ test->loadHTML(
+ "<html><body>"
+ "<input style=\"position:absolute; left:0; top:0; margin:0; padding:0\" type=\"button\" value=\"click to show a popup\" onclick=\"window.open('data:foo');\"/>"
+ "<a style=\"position:absolute; left:20; top:20;\" href=\"data:bar\" target=\"_blank\">popup link</a>"
+ "</body></html>");
+ test->waitUntilLoadFinished();
+
+ // Click on a button.
+ test->clickAndWaitUntilMainLoopFinishes(5, 5);
+ g_assert_cmpstr(webkit_uri_request_get_uri(webkit_navigation_action_get_request(test->m_navigation)), ==, "data:foo");
+ g_assert_cmpuint(webkit_navigation_action_get_navigation_type(test->m_navigation), ==, WEBKIT_NAVIGATION_TYPE_OTHER);
+ // FIXME: This should be button 1.
+ g_assert_cmpuint(webkit_navigation_action_get_mouse_button(test->m_navigation), ==, 0);
+ g_assert_cmpuint(webkit_navigation_action_get_modifiers(test->m_navigation), ==, 0);
+ g_assert(webkit_navigation_action_is_user_gesture(test->m_navigation));
+
+ // Click on a link.
+ test->clickAndWaitUntilMainLoopFinishes(21, 21);
+ g_assert_cmpstr(webkit_uri_request_get_uri(webkit_navigation_action_get_request(test->m_navigation)), ==, "data:bar");
+ g_assert_cmpuint(webkit_navigation_action_get_navigation_type(test->m_navigation), ==, WEBKIT_NAVIGATION_TYPE_LINK_CLICKED);
+ g_assert_cmpuint(webkit_navigation_action_get_mouse_button(test->m_navigation), ==, 1);
+ g_assert_cmpuint(webkit_navigation_action_get_modifiers(test->m_navigation), ==, 0);
+ g_assert(webkit_navigation_action_is_user_gesture(test->m_navigation));
+
+ // No user interaction.
+ test->loadHTML("<html><body onLoad=\"window.open();\"></html>");
+ test->waitUntilMainLoopFinishes();
+
+ g_assert_cmpstr(webkit_uri_request_get_uri(webkit_navigation_action_get_request(test->m_navigation)), ==, "");
+ g_assert_cmpuint(webkit_navigation_action_get_navigation_type(test->m_navigation), ==, WEBKIT_NAVIGATION_TYPE_OTHER);
+ g_assert_cmpuint(webkit_navigation_action_get_mouse_button(test->m_navigation), ==, 0);
+ g_assert_cmpuint(webkit_navigation_action_get_modifiers(test->m_navigation), ==, 0);
+ g_assert(!webkit_navigation_action_is_user_gesture(test->m_navigation));
+}
+
+static gboolean checkMimeTypeForFilter(GtkFileFilter* filter, const gchar* mimeType)
+{
+ GtkFileFilterInfo filterInfo;
+ filterInfo.contains = GTK_FILE_FILTER_MIME_TYPE;
+ filterInfo.mime_type = mimeType;
+ return gtk_file_filter_filter(filter, &filterInfo);
+}
+
+class ModalDialogsTest: public UIClientTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ModalDialogsTest);
+
+ static void dialogRunAsModalCallback(WebKitWebView* webView, ModalDialogsTest* test)
+ {
+ g_assert(webView != test->m_webView);
+ test->m_webViewEvents.append(RunAsModal);
+ }
+
+ GtkWidget* viewCreate(WebKitWebView* webView, WebKitNavigationAction* navigation)
+ {
+ g_assert(webView == m_webView);
+
+ GtkWidget* newWebView = UIClientTest::viewCreate(webView, navigation);
+ g_signal_connect(newWebView, "run-as-modal", G_CALLBACK(dialogRunAsModalCallback), this);
+ return newWebView;
+ }
+
+ void viewReadyToShow(WebKitWebView* webView)
+ {
+ g_assert(webView != m_webView);
+ m_webViewEvents.append(ReadyToShow);
+ }
+};
+
+static void testWebViewAllowModalDialogs(ModalDialogsTest* test, gconstpointer)
+{
+ WebKitSettings* settings = webkit_web_view_get_settings(test->m_webView);
+ webkit_settings_set_allow_modal_dialogs(settings, TRUE);
+
+ test->loadHtml("<html><body onload=\"window.showModalDialog('data:text/html,<html><body/><script>window.close();</script></html>')\"></body></html>", 0);
+ test->waitUntilMainLoopFinishes();
+
+ Vector<UIClientTest::WebViewEvents>& events = test->m_webViewEvents;
+ g_assert_cmpint(events.size(), ==, 4);
+ g_assert_cmpint(events[0], ==, UIClientTest::Create);
+ g_assert_cmpint(events[1], ==, UIClientTest::ReadyToShow);
+ g_assert_cmpint(events[2], ==, UIClientTest::RunAsModal);
+ g_assert_cmpint(events[3], ==, UIClientTest::Close);
+}
+
+static void testWebViewDisallowModalDialogs(ModalDialogsTest* test, gconstpointer)
+{
+ WebKitSettings* settings = webkit_web_view_get_settings(test->m_webView);
+ webkit_settings_set_allow_modal_dialogs(settings, FALSE);
+
+ test->loadHtml("<html><body onload=\"window.showModalDialog('data:text/html,<html><body/><script>window.close();</script></html>')\"></body></html>", 0);
+ // We need to use a timeout here because the viewClose() function
+ // won't ever be called as the dialog won't be created.
+ test->wait(1);
+
+ Vector<UIClientTest::WebViewEvents>& events = test->m_webViewEvents;
+ g_assert_cmpint(events.size(), ==, 0);
+}
+
+static void testWebViewJavaScriptDialogs(UIClientTest* test, gconstpointer)
+{
+ static const char* htmlOnLoadFormat = "<html><body onLoad=\"%s\"></body></html>";
+ static const char* jsAlertFormat = "alert('%s')";
+ static const char* jsConfirmFormat = "do { confirmed = confirm('%s'); } while (!confirmed); alert('confirmed');";
+ static const char* jsPromptFormat = "alert(prompt('%s', 'default'));";
+
+ test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_ALERT;
+ GUniquePtr<char> alertDialogMessage(g_strdup_printf(jsAlertFormat, kAlertDialogMessage));
+ GUniquePtr<char> alertHTML(g_strdup_printf(htmlOnLoadFormat, alertDialogMessage.get()));
+ test->loadHtml(alertHTML.get(), 0);
+ test->waitUntilMainLoopFinishes();
+
+ test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_CONFIRM;
+ GUniquePtr<char> confirmDialogMessage(g_strdup_printf(jsConfirmFormat, kConfirmDialogMessage));
+ GUniquePtr<char> confirmHTML(g_strdup_printf(htmlOnLoadFormat, confirmDialogMessage.get()));
+ test->loadHtml(confirmHTML.get(), 0);
+ test->waitUntilMainLoopFinishes();
+
+ test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_PROMPT;
+ GUniquePtr<char> promptDialogMessage(g_strdup_printf(jsPromptFormat, kPromptDialogMessage));
+ GUniquePtr<char> promptHTML(g_strdup_printf(htmlOnLoadFormat, promptDialogMessage.get()));
+ test->loadHtml(promptHTML.get(), 0);
+ test->waitUntilMainLoopFinishes();
+}
+
+static void testWebViewWindowProperties(UIClientTest* test, gconstpointer)
+{
+ static const char* windowProrpertiesString = "left=100,top=150,width=400,height=400,location=no,menubar=no,status=no,toolbar=no,scrollbars=no";
+ GdkRectangle geometry = { 100, 150, 400, 400 };
+ test->setExpectedWindowProperties(UIClientTest::WindowProperties(&geometry, false, false, false, false, false, true, false));
+
+ GUniquePtr<char> htmlString(g_strdup_printf("<html><body onLoad=\"window.open('', '', '%s').close();\"></body></html>", windowProrpertiesString));
+ test->loadHtml(htmlString.get(), 0);
+ test->waitUntilMainLoopFinishes();
+
+ static const char* propertiesChanged[] = {
+ "geometry", "locationbar-visible", "menubar-visible", "statusbar-visible", "toolbar-visible", "scrollbars-visible"
+ };
+ for (size_t i = 0; i < G_N_ELEMENTS(propertiesChanged); ++i)
+ g_assert(test->m_windowPropertiesChanged.contains(propertiesChanged[i]));
+
+ Vector<UIClientTest::WebViewEvents>& events = test->m_webViewEvents;
+ g_assert_cmpint(events.size(), ==, 3);
+ g_assert_cmpint(events[0], ==, UIClientTest::Create);
+ g_assert_cmpint(events[1], ==, UIClientTest::ReadyToShow);
+ g_assert_cmpint(events[2], ==, UIClientTest::Close);
+}
+
+static void testWebViewMouseTarget(UIClientTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
+
+ const char* linksHoveredHTML =
+ "<html><head>"
+ " <script>"
+ " window.onload = function () {"
+ " window.getSelection().removeAllRanges();"
+ " var select_range = document.createRange();"
+ " select_range.selectNodeContents(document.getElementById('text_to_select'));"
+ " window.getSelection().addRange(select_range);"
+ " }"
+ " </script>"
+ "</head><body>"
+ " <a style='position:absolute; left:1; top:1' href='http://www.webkitgtk.org' title='WebKitGTK+ Title'>WebKitGTK+ Website</a>"
+ " <img style='position:absolute; left:1; top:10' src='0xdeadbeef' width=5 height=5></img>"
+ " <a style='position:absolute; left:1; top:20' href='http://www.webkitgtk.org/logo' title='WebKitGTK+ Logo'><img src='0xdeadbeef' width=5 height=5></img></a>"
+ " <input style='position:absolute; left:1; top:30' size='10'></input>"
+ " <div style='position:absolute; left:1; top:50; width:30; height:30; overflow:scroll'>&nbsp;</div>"
+ " <video style='position:absolute; left:1; top:100' width='300' height='300' controls='controls' preload='none'><source src='movie.ogg' type='video/ogg' /></video>"
+ " <p style='position:absolute; left:1; top:120' id='text_to_select'>Lorem ipsum.</p>"
+ "</body></html>";
+
+ test->loadHtml(linksHoveredHTML, "file:///");
+ test->waitUntilLoadFinished();
+
+ // Move over link.
+ WebKitHitTestResult* hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(1, 1);
+ g_assert(webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ g_assert_cmpstr(webkit_hit_test_result_get_link_uri(hitTestResult), ==, "http://www.webkitgtk.org/");
+ g_assert_cmpstr(webkit_hit_test_result_get_link_title(hitTestResult), ==, "WebKitGTK+ Title");
+ g_assert_cmpstr(webkit_hit_test_result_get_link_label(hitTestResult), ==, "WebKitGTK+ Website");
+ g_assert(!test->m_mouseTargetModifiers);
+
+ // Move out of the link.
+ hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(0, 0);
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ g_assert(!test->m_mouseTargetModifiers);
+
+ // Move over image with GDK_CONTROL_MASK.
+ hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(1, 10, GDK_CONTROL_MASK);
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_scrollbar(hitTestResult));
+ g_assert_cmpstr(webkit_hit_test_result_get_image_uri(hitTestResult), ==, "file:///0xdeadbeef");
+ g_assert(test->m_mouseTargetModifiers & GDK_CONTROL_MASK);
+
+ // Move over image link.
+ hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(1, 20);
+ g_assert(webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_scrollbar(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ g_assert_cmpstr(webkit_hit_test_result_get_link_uri(hitTestResult), ==, "http://www.webkitgtk.org/logo");
+ g_assert_cmpstr(webkit_hit_test_result_get_image_uri(hitTestResult), ==, "file:///0xdeadbeef");
+ g_assert_cmpstr(webkit_hit_test_result_get_link_title(hitTestResult), ==, "WebKitGTK+ Logo");
+ g_assert(!webkit_hit_test_result_get_link_label(hitTestResult));
+ g_assert(!test->m_mouseTargetModifiers);
+
+ // Move over media.
+ hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(1, 100);
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_scrollbar(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ g_assert_cmpstr(webkit_hit_test_result_get_media_uri(hitTestResult), ==, "file:///movie.ogg");
+ g_assert(!test->m_mouseTargetModifiers);
+
+ // Mover over input.
+ hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(5, 35);
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_scrollbar(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ g_assert(!test->m_mouseTargetModifiers);
+
+ // Move over scrollbar.
+ hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(5, 75);
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_scrollbar(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
+ g_assert(!test->m_mouseTargetModifiers);
+
+ // Move over selection.
+ hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(2, 145);
+ g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
+ g_assert(!webkit_hit_test_result_context_is_scrollbar(hitTestResult));
+ g_assert(webkit_hit_test_result_context_is_selection(hitTestResult));
+ g_assert(!test->m_mouseTargetModifiers);
+
+}
+
+static void testWebViewGeolocationPermissionRequests(UIClientTest* test, gconstpointer)
+{
+ // Some versions of geoclue give a runtime warning because it tries
+ // to register the error quark twice. See https://bugs.webkit.org/show_bug.cgi?id=89858.
+ // Make warnings non-fatal for this test to make it pass.
+ test->removeLogFatalFlag(G_LOG_LEVEL_WARNING);
+ test->showInWindowAndWaitUntilMapped();
+ static const char* geolocationRequestHTML =
+ "<html>"
+ " <script>"
+ " function runTest()"
+ " {"
+ " navigator.geolocation.getCurrentPosition(function(p) { document.title = \"OK\" },"
+ " function(e) { document.title = e.code });"
+ " }"
+ " </script>"
+ " <body onload='runTest();'></body>"
+ "</html>";
+
+ // Test denying a permission request.
+ test->m_allowPermissionRequests = false;
+ test->loadHtml(geolocationRequestHTML, 0);
+ test->waitUntilTitleChanged();
+
+ // According to the Geolocation API specification, '1' is the
+ // error code returned for the PERMISSION_DENIED error.
+ // http://dev.w3.org/geo/api/spec-source.html#position_error_interface
+ const gchar* result = webkit_web_view_get_title(test->m_webView);
+ g_assert_cmpstr(result, ==, "1");
+
+ // Test allowing a permission request.
+ test->m_allowPermissionRequests = true;
+ test->loadHtml(geolocationRequestHTML, 0);
+ test->waitUntilTitleChanged();
+
+ // Check that we did not get the PERMISSION_DENIED error now.
+ result = webkit_web_view_get_title(test->m_webView);
+ g_assert_cmpstr(result, !=, "1");
+ test->addLogFatalFlag(G_LOG_LEVEL_WARNING);
+}
+
+#if ENABLE(MEDIA_STREAM)
+static void testWebViewUserMediaPermissionRequests(UIClientTest* test, gconstpointer)
+{
+ WebKitSettings* settings = webkit_web_view_get_settings(test->m_webView);
+ gboolean enabled = webkit_settings_get_enable_media_stream(settings);
+ webkit_settings_set_enable_media_stream(settings, TRUE);
+
+ test->showInWindowAndWaitUntilMapped();
+ static const char* userMediaRequestHTML =
+ "<html>"
+ " <script>"
+ " function runTest()"
+ " {"
+ " navigator.webkitGetUserMedia({audio: true, video: true},"
+ " function(s) { document.title = \"OK\" },"
+ " function(e) { document.title = e.name });"
+ " }"
+ " </script>"
+ " <body onload='runTest();'></body>"
+ "</html>";
+
+ test->m_verifyMediaTypes = TRUE;
+ test->m_expectedAudioMedia = TRUE;
+ test->m_expectedVideoMedia = TRUE;
+
+ // Test denying a permission request.
+ test->m_allowPermissionRequests = false;
+ test->loadHtml(userMediaRequestHTML, nullptr);
+ test->waitUntilTitleChangedTo("PermissionDeniedError");
+
+ // Test allowing a permission request.
+ test->m_allowPermissionRequests = true;
+ test->loadHtml(userMediaRequestHTML, nullptr);
+ test->waitUntilTitleChangedTo("OK");
+
+ webkit_settings_set_enable_media_stream(settings, enabled);
+}
+
+static void testWebViewAudioOnlyUserMediaPermissionRequests(UIClientTest* test, gconstpointer)
+{
+ WebKitSettings* settings = webkit_web_view_get_settings(test->m_webView);
+ gboolean enabled = webkit_settings_get_enable_media_stream(settings);
+ webkit_settings_set_enable_media_stream(settings, TRUE);
+
+ test->showInWindowAndWaitUntilMapped();
+ static const char* userMediaRequestHTML =
+ "<html>"
+ " <script>"
+ " function runTest()"
+ " {"
+ " navigator.webkitGetUserMedia({audio: true, video: false},"
+ " function(s) { document.title = \"OK\" },"
+ " function(e) { document.title = e.name });"
+ " }"
+ " </script>"
+ " <body onload='runTest();'></body>"
+ "</html>";
+
+ test->m_verifyMediaTypes = TRUE;
+ test->m_expectedAudioMedia = TRUE;
+ test->m_expectedVideoMedia = FALSE;
+
+ // Test denying a permission request.
+ test->m_allowPermissionRequests = false;
+ test->loadHtml(userMediaRequestHTML, nullptr);
+ test->waitUntilTitleChangedTo("PermissionDeniedError");
+
+ webkit_settings_set_enable_media_stream(settings, enabled);
+}
+#endif // ENABLE(MEDIA_STREAM)
+
+class FileChooserTest: public UIClientTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(FileChooserTest);
+
+ FileChooserTest()
+ {
+ g_signal_connect(m_webView, "run-file-chooser", G_CALLBACK(runFileChooserCallback), this);
+ }
+
+ static gboolean runFileChooserCallback(WebKitWebView*, WebKitFileChooserRequest* request, FileChooserTest* test)
+ {
+ test->runFileChooser(request);
+ return TRUE;
+ }
+
+ void runFileChooser(WebKitFileChooserRequest* request)
+ {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
+ m_fileChooserRequest = request;
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ WebKitFileChooserRequest* clickMouseButtonAndWaitForFileChooserRequest(int x, int y)
+ {
+ clickMouseButton(x, y);
+ g_main_loop_run(m_mainLoop);
+ return m_fileChooserRequest.get();
+ }
+
+private:
+ GRefPtr<WebKitFileChooserRequest> m_fileChooserRequest;
+};
+
+static void testWebViewFileChooserRequest(FileChooserTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+ static const char* fileChooserHTMLFormat = "<html><body><input style='position:absolute;left:0;top:0;margin:0;padding:0' type='file' %s/></body></html>";
+
+ // Multiple selections not allowed, no MIME filtering.
+ GUniquePtr<char> simpleFileUploadHTML(g_strdup_printf(fileChooserHTMLFormat, ""));
+ test->loadHtml(simpleFileUploadHTML.get(), 0);
+ test->waitUntilLoadFinished();
+ WebKitFileChooserRequest* fileChooserRequest = test->clickMouseButtonAndWaitForFileChooserRequest(5, 5);
+ g_assert(!webkit_file_chooser_request_get_select_multiple(fileChooserRequest));
+
+ const gchar* const* mimeTypes = webkit_file_chooser_request_get_mime_types(fileChooserRequest);
+ g_assert(!mimeTypes);
+ GtkFileFilter* filter = webkit_file_chooser_request_get_mime_types_filter(fileChooserRequest);
+ g_assert(!filter);
+ const gchar* const* selectedFiles = webkit_file_chooser_request_get_selected_files(fileChooserRequest);
+ g_assert(!selectedFiles);
+ webkit_file_chooser_request_cancel(fileChooserRequest);
+
+ // Multiple selections allowed, no MIME filtering, some pre-selected files.
+ GUniquePtr<char> multipleSelectionFileUploadHTML(g_strdup_printf(fileChooserHTMLFormat, "multiple"));
+ test->loadHtml(multipleSelectionFileUploadHTML.get(), 0);
+ test->waitUntilLoadFinished();
+ fileChooserRequest = test->clickMouseButtonAndWaitForFileChooserRequest(5, 5);
+ g_assert(webkit_file_chooser_request_get_select_multiple(fileChooserRequest));
+
+ mimeTypes = webkit_file_chooser_request_get_mime_types(fileChooserRequest);
+ g_assert(!mimeTypes);
+ filter = webkit_file_chooser_request_get_mime_types_filter(fileChooserRequest);
+ g_assert(!filter);
+ selectedFiles = webkit_file_chooser_request_get_selected_files(fileChooserRequest);
+ g_assert(!selectedFiles);
+
+ // Select some files.
+ const gchar* filesToSelect[4] = { "/foo", "/foo/bar", "/foo/bar/baz", 0 };
+ webkit_file_chooser_request_select_files(fileChooserRequest, filesToSelect);
+
+ // Check the files that have been just selected.
+ selectedFiles = webkit_file_chooser_request_get_selected_files(fileChooserRequest);
+ g_assert(selectedFiles);
+ g_assert_cmpstr(selectedFiles[0], ==, "/foo");
+ g_assert_cmpstr(selectedFiles[1], ==, "/foo/bar");
+ g_assert_cmpstr(selectedFiles[2], ==, "/foo/bar/baz");
+ g_assert(!selectedFiles[3]);
+
+ // Perform another request to check if the list of files selected
+ // in the previous step appears now as part of the new request.
+ fileChooserRequest = test->clickMouseButtonAndWaitForFileChooserRequest(5, 5);
+ selectedFiles = webkit_file_chooser_request_get_selected_files(fileChooserRequest);
+ g_assert(selectedFiles);
+ g_assert_cmpstr(selectedFiles[0], ==, "/foo");
+ g_assert_cmpstr(selectedFiles[1], ==, "/foo/bar");
+ g_assert_cmpstr(selectedFiles[2], ==, "/foo/bar/baz");
+ g_assert(!selectedFiles[3]);
+ webkit_file_chooser_request_cancel(fileChooserRequest);
+
+ // Multiple selections not allowed, only accept images, audio and video files..
+ GUniquePtr<char> mimeFilteredFileUploadHTML(g_strdup_printf(fileChooserHTMLFormat, "accept='audio/*,video/*,image/*'"));
+ test->loadHtml(mimeFilteredFileUploadHTML.get(), 0);
+ test->waitUntilLoadFinished();
+ fileChooserRequest = test->clickMouseButtonAndWaitForFileChooserRequest(5, 5);
+ g_assert(!webkit_file_chooser_request_get_select_multiple(fileChooserRequest));
+
+ mimeTypes = webkit_file_chooser_request_get_mime_types(fileChooserRequest);
+ g_assert(mimeTypes);
+ g_assert_cmpstr(mimeTypes[0], ==, "audio/*");
+ g_assert_cmpstr(mimeTypes[1], ==, "video/*");
+ g_assert_cmpstr(mimeTypes[2], ==, "image/*");
+ g_assert(!mimeTypes[3]);
+
+ filter = webkit_file_chooser_request_get_mime_types_filter(fileChooserRequest);
+ g_assert(GTK_IS_FILE_FILTER(filter));
+ g_assert(checkMimeTypeForFilter(filter, "audio/*"));
+ g_assert(checkMimeTypeForFilter(filter, "video/*"));
+ g_assert(checkMimeTypeForFilter(filter, "image/*"));
+
+ selectedFiles = webkit_file_chooser_request_get_selected_files(fileChooserRequest);
+ g_assert(!selectedFiles);
+ webkit_file_chooser_request_cancel(fileChooserRequest);
+}
+
+class ColorChooserTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ColorChooserTest);
+
+ static gboolean runColorChooserCallback(WebKitWebView*, WebKitColorChooserRequest* request, ColorChooserTest* test)
+ {
+ test->runColorChooser(request);
+ return TRUE;
+ }
+
+ static void requestFinishedCallback(WebKitColorChooserRequest* request, ColorChooserTest* test)
+ {
+ g_assert(test->m_request.get() == request);
+ test->m_request = nullptr;
+ if (g_main_loop_is_running(test->m_mainLoop))
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ ColorChooserTest()
+ {
+ g_signal_connect(m_webView, "run-color-chooser", G_CALLBACK(runColorChooserCallback), this);
+ }
+
+ void runColorChooser(WebKitColorChooserRequest* request)
+ {
+ g_assert(WEBKIT_IS_COLOR_CHOOSER_REQUEST(request));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
+ m_request = request;
+ g_signal_connect(request, "finished", G_CALLBACK(requestFinishedCallback), this);
+ g_main_loop_quit(m_mainLoop);
+ }
+
+ void finishRequest()
+ {
+ g_assert(m_request.get());
+ webkit_color_chooser_request_finish(m_request.get());
+ g_assert(!m_request);
+ }
+
+ void cancelRequest()
+ {
+ g_assert(m_request.get());
+ webkit_color_chooser_request_cancel(m_request.get());
+ g_assert(!m_request);
+ }
+
+ WebKitColorChooserRequest* clickMouseButtonAndWaitForColorChooserRequest(int x, int y)
+ {
+ clickMouseButton(x, y);
+ g_main_loop_run(m_mainLoop);
+ g_assert(m_request.get());
+ return m_request.get();
+ }
+
+private:
+ GRefPtr<WebKitColorChooserRequest> m_request;
+};
+
+static void testWebViewColorChooserRequest(ColorChooserTest* test, gconstpointer)
+{
+ static const char* colorChooserHTMLFormat = "<html><body><input style='position:absolute;left:1;top:1;margin:0;padding:0;width:45;height:25' type='color' %s/></body></html>";
+ test->showInWindowAndWaitUntilMapped();
+
+ GUniquePtr<char> defaultColorHTML(g_strdup_printf(colorChooserHTMLFormat, ""));
+ test->loadHtml(defaultColorHTML.get(), nullptr);
+ test->waitUntilLoadFinished();
+ WebKitColorChooserRequest* request = test->clickMouseButtonAndWaitForColorChooserRequest(5, 5);
+
+ // Default color is black (#000000).
+ GdkRGBA rgba1;
+ GdkRGBA rgba2 = { 0., 0., 0., 1. };
+ webkit_color_chooser_request_get_rgba(request, &rgba1);
+ g_assert(gdk_rgba_equal(&rgba1, &rgba2));
+
+ // Set a different color.
+ rgba2.green = 1;
+ webkit_color_chooser_request_set_rgba(request, &rgba2);
+ webkit_color_chooser_request_get_rgba(request, &rgba1);
+ g_assert(gdk_rgba_equal(&rgba1, &rgba2));
+
+ GdkRectangle rect;
+ webkit_color_chooser_request_get_element_rectangle(request, &rect);
+ g_assert_cmpint(rect.x, == , 1);
+ g_assert_cmpint(rect.y, == , 1);
+ g_assert_cmpint(rect.width, == , 45);
+ g_assert_cmpint(rect.height, == , 25);
+
+ test->finishRequest();
+
+ // Use an initial color.
+ GUniquePtr<char> initialColorHTML(g_strdup_printf(colorChooserHTMLFormat, "value='#FF00FF'"));
+ test->loadHtml(initialColorHTML.get(), nullptr);
+ test->waitUntilLoadFinished();
+ request = test->clickMouseButtonAndWaitForColorChooserRequest(5, 5);
+
+ webkit_color_chooser_request_get_rgba(request, &rgba1);
+ GdkRGBA rgba3 = { 1., 0., 1., 1. };
+ g_assert(gdk_rgba_equal(&rgba1, &rgba3));
+
+ test->cancelRequest();
+}
+
+void beforeAll()
+{
+ UIClientTest::add("WebKitWebView", "create-ready-close", testWebViewCreateReadyClose);
+ CreateNavigationDataTest::add("WebKitWebView", "create-navigation-data", testWebViewCreateNavigationData);
+ ModalDialogsTest::add("WebKitWebView", "allow-modal-dialogs", testWebViewAllowModalDialogs);
+ ModalDialogsTest::add("WebKitWebView", "disallow-modal-dialogs", testWebViewDisallowModalDialogs);
+ UIClientTest::add("WebKitWebView", "javascript-dialogs", testWebViewJavaScriptDialogs);
+ UIClientTest::add("WebKitWebView", "window-properties", testWebViewWindowProperties);
+ UIClientTest::add("WebKitWebView", "mouse-target", testWebViewMouseTarget);
+ UIClientTest::add("WebKitWebView", "geolocation-permission-requests", testWebViewGeolocationPermissionRequests);
+#if ENABLE(MEDIA_STREAM)
+ UIClientTest::add("WebKitWebView", "usermedia-permission-requests", testWebViewUserMediaPermissionRequests);
+ UIClientTest::add("WebKitWebView", "audio-usermedia-permission-request", testWebViewAudioOnlyUserMediaPermissionRequests);
+#endif
+ FileChooserTest::add("WebKitWebView", "file-chooser-request", testWebViewFileChooserRequest);
+ ColorChooserTest::add("WebKitWebView", "color-chooser-request", testWebViewColorChooserRequest);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebExtensions.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebExtensions.cpp
new file mode 100644
index 000000000..b41e5eb82
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebExtensions.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebKitTestBus.h"
+#include "WebViewTest.h"
+#include <wtf/glib/GRefPtr.h>
+
+static const char* webExtensionsUserData = "Web Extensions user data";
+static WebKitTestBus* bus;
+static GUniquePtr<char> scriptDialogResult;
+
+static void testWebExtensionGetTitle(WebViewTest* test, gconstpointer)
+{
+ test->loadHtml("<html><head><title>WebKitGTK+ Web Extensions Test</title></head><body></body></html>", 0);
+ test->waitUntilLoadFinished();
+
+ GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID));
+ GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(),
+ "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", test->m_mainLoop));
+ GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync(
+ proxy.get(),
+ "GetTitle",
+ g_variant_new("(t)", webkit_web_view_get_page_id(test->m_webView)),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, 0, 0));
+ g_assert(result);
+
+ const char* title;
+ g_variant_get(result.get(), "(&s)", &title);
+ g_assert_cmpstr(title, ==, "WebKitGTK+ Web Extensions Test");
+}
+
+static void documentLoadedCallback(GDBusConnection*, const char*, const char*, const char*, const char*, GVariant*, WebViewTest* test)
+{
+ g_main_loop_quit(test->m_mainLoop);
+}
+
+static void testDocumentLoadedSignal(WebViewTest* test, gconstpointer)
+{
+ GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID));
+ GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(),
+ "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", test->m_mainLoop));
+ GDBusConnection* connection = g_dbus_proxy_get_connection(proxy.get());
+ guint id = g_dbus_connection_signal_subscribe(connection,
+ 0,
+ "org.webkit.gtk.WebExtensionTest",
+ "DocumentLoaded",
+ "/org/webkit/gtk/WebExtensionTest",
+ 0,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ reinterpret_cast<GDBusSignalCallback>(documentLoadedCallback),
+ test,
+ 0);
+ g_assert(id);
+
+ test->loadHtml("<html><head><title>WebKitGTK+ Web Extensions Test</title></head><body></body></html>", 0);
+ g_main_loop_run(test->m_mainLoop);
+ g_dbus_connection_signal_unsubscribe(connection, id);
+}
+
+static gboolean webProcessCrashedCallback(WebKitWebView*, WebViewTest* test)
+{
+ test->quitMainLoop();
+
+ return FALSE;
+}
+
+static void testWebKitWebViewProcessCrashed(WebViewTest* test, gconstpointer)
+{
+ test->loadHtml("<html></html>", 0);
+ test->waitUntilLoadFinished();
+
+ g_signal_connect_after(test->m_webView, "web-process-crashed",
+ G_CALLBACK(webProcessCrashedCallback), test);
+
+ test->m_expectedWebProcessCrash = true;
+
+ GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID));
+ GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(),
+ "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", test->m_mainLoop));
+
+ GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync(
+ proxy.get(),
+ "AbortProcess",
+ 0,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, 0, 0));
+ g_assert(!result);
+ g_main_loop_run(test->m_mainLoop);
+ test->m_expectedWebProcessCrash = false;
+}
+
+static void testWebExtensionWindowObjectCleared(WebViewTest* test, gconstpointer)
+{
+ test->loadHtml("<html><header></header><body></body></html>", 0);
+ test->waitUntilLoadFinished();
+
+ GUniqueOutPtr<GError> error;
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.echo('Foo');", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "Foo");
+}
+
+static gboolean scriptDialogCallback(WebKitWebView*, WebKitScriptDialog* dialog, gpointer)
+{
+ g_assert_cmpuint(webkit_script_dialog_get_dialog_type(dialog), ==, WEBKIT_SCRIPT_DIALOG_ALERT);
+ scriptDialogResult.reset(g_strdup(webkit_script_dialog_get_message(dialog)));
+ return TRUE;
+}
+
+static void runJavaScriptInIsolatedWorldFinishedCallback(GDBusProxy* proxy, GAsyncResult* result, WebViewTest* test)
+{
+ g_dbus_proxy_call_finish(proxy, result, 0);
+ g_main_loop_quit(test->m_mainLoop);
+}
+
+static void testWebExtensionIsolatedWorld(WebViewTest* test, gconstpointer)
+{
+ test->loadHtml("<html><header></header><body><div id='console'></div></body></html>", 0);
+ test->waitUntilLoadFinished();
+
+ gulong scriptDialogID = g_signal_connect(test->m_webView, "script-dialog", G_CALLBACK(scriptDialogCallback), nullptr);
+
+ static const char* mainWorldScript =
+ "top.foo = 'Foo';\n"
+ "document.getElementById('console').innerHTML = top.foo;\n"
+ "window.open = function () { alert('Main World'); }\n"
+ "document.open(1, 2, 3);";
+ test->runJavaScriptAndWaitUntilFinished(mainWorldScript, 0);
+ g_assert_cmpstr(scriptDialogResult.get(), ==, "Main World");
+
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementById('console').innerHTML", 0);
+ g_assert(javascriptResult);
+ GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "Foo");
+
+ static const char* isolatedWorldScript =
+ "document.getElementById('console').innerHTML = top.foo;\n"
+ "window.open = function () { alert('Isolated World'); }\n"
+ "document.open(1, 2, 3);";
+ GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID));
+ GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(),
+ "/org/webkit/gtk/WebExtensionTest" , "org.webkit.gtk.WebExtensionTest", test->m_mainLoop));
+ g_dbus_proxy_call(proxy.get(),
+ "RunJavaScriptInIsolatedWorld",
+ g_variant_new("(t&s)", webkit_web_view_get_page_id(test->m_webView), isolatedWorldScript),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, 0,
+ reinterpret_cast<GAsyncReadyCallback>(runJavaScriptInIsolatedWorldFinishedCallback),
+ test);
+ g_main_loop_run(test->m_mainLoop);
+ g_assert_cmpstr(scriptDialogResult.get(), ==, "Isolated World");
+
+ // Check that 'top.foo' defined in main world is not visible in isolated world.
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementById('console').innerHTML", 0);
+ g_assert(javascriptResult);
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "undefined");
+
+ g_signal_handler_disconnect(test->m_webView, scriptDialogID);
+}
+
+static gboolean permissionRequestCallback(WebKitWebView*, WebKitPermissionRequest* request, WebViewTest* test)
+{
+ if (!WEBKIT_IS_INSTALL_MISSING_MEDIA_PLUGINS_PERMISSION_REQUEST(request))
+ return FALSE;
+
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
+ WebKitInstallMissingMediaPluginsPermissionRequest* missingPluginsRequest = WEBKIT_INSTALL_MISSING_MEDIA_PLUGINS_PERMISSION_REQUEST(request);
+ g_assert(webkit_install_missing_media_plugins_permission_request_get_description(missingPluginsRequest));
+ webkit_permission_request_deny(request);
+ test->quitMainLoop();
+
+ return TRUE;
+}
+
+static void testInstallMissingPluginsPermissionRequest(WebViewTest* test, gconstpointer)
+{
+ GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID));
+ GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(),
+ "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", test->m_mainLoop));
+ GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync(proxy.get(), "RemoveAVPluginsFromGSTRegistry",
+ nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, nullptr));
+
+ test->showInWindowAndWaitUntilMapped();
+
+ gulong permissionRequestSignalID = g_signal_connect(test->m_webView, "permission-request", G_CALLBACK(permissionRequestCallback), test);
+ // FIXME: the base URI needs to finish with / to work, that shouldn't happen.
+ GUniquePtr<char> baseURI(g_strconcat("file://", Test::getResourcesDir(Test::WebKit2Resources).data(), "/", nullptr));
+ test->loadHtml("<html><body><video src=\"test.mp4\" autoplay></video></body></html>", baseURI.get());
+ g_main_loop_run(test->m_mainLoop);
+ g_signal_handler_disconnect(test->m_webView, permissionRequestSignalID);
+}
+
+void beforeAll()
+{
+ bus = new WebKitTestBus();
+ if (!bus->run())
+ return;
+
+ WebViewTest::add("WebKitWebExtension", "dom-document-title", testWebExtensionGetTitle);
+ WebViewTest::add("WebKitWebExtension", "document-loaded-signal", testDocumentLoadedSignal);
+ WebViewTest::add("WebKitWebView", "web-process-crashed", testWebKitWebViewProcessCrashed);
+ WebViewTest::add("WebKitWebExtension", "window-object-cleared", testWebExtensionWindowObjectCleared);
+ WebViewTest::add("WebKitWebExtension", "isolated-world", testWebExtensionIsolatedWorld);
+ WebViewTest::add("WebKitWebView", "install-missing-plugins-permission-request", testInstallMissingPluginsPermissionRequest);
+}
+
+void afterAll()
+{
+ delete bus;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitAccessibility.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitAccessibility.cpp
new file mode 100644
index 000000000..ea4a8125f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitAccessibility.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "TestMain.h"
+#include "WebViewTest.h"
+
+// The libatspi headers don't use G_BEGIN_DECLS
+extern "C" {
+#include <atspi/atspi.h>
+}
+
+#include <errno.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+
+// Name of the test server application creating the webView object.
+static const char* kTestServerAppName = "AccessibilityTestServer";
+
+// Max seconds to wait for the test server before inspecting it.
+static const int kMaxWaitForChild = 5;
+
+// The PID for the test server running, so we can kill it if needed.
+static GPid kChildProcessPid = 0;
+
+// Whether the child has replied and it's ready.
+static bool kChildIsReady = false;
+
+static void stopTestServer()
+{
+ // Do nothing if there's no server running.
+ if (!kChildProcessPid)
+ return;
+
+ g_spawn_close_pid(kChildProcessPid);
+ kill(kChildProcessPid, SIGTERM);
+ kChildProcessPid = 0;
+}
+
+static void sigAbortHandler(int sigNum)
+{
+ // Just stop the test server if SIGABRT was received.
+ stopTestServer();
+}
+
+static gpointer testServerMonitorThreadFunc(gpointer)
+{
+ // Wait for the specified timeout to happen.
+ g_usleep(kMaxWaitForChild * G_USEC_PER_SEC);
+
+ // Kill the child process if not ready yet.
+ if (!kChildIsReady)
+ stopTestServer();
+
+ g_thread_exit(0);
+ return 0;
+}
+
+static void startTestServerMonitor()
+{
+ kChildIsReady = false;
+ g_thread_new("TestServerMonitor", testServerMonitorThreadFunc, 0);
+}
+
+static void startTestServer()
+{
+ // Prepare argv[] for spawning the server process.
+ GUniquePtr<char> testServerPath(g_build_filename(WEBKIT_EXEC_PATH, "TestWebKitAPI", "WebKit2Gtk", kTestServerAppName, nullptr));
+
+ char* testServerArgv[2];
+ testServerArgv[0] = testServerPath.get();
+ testServerArgv[1] = 0;
+
+ // Spawn the server, getting its stdout file descriptor to set a
+ // communication channel, so we know when it's ready.
+ int childStdout = 0;
+ if (!g_spawn_async_with_pipes(0, testServerArgv, 0, static_cast<GSpawnFlags>(0), 0, 0, &kChildProcessPid, 0, &childStdout, 0, 0)) {
+ close(childStdout);
+ return;
+ }
+
+ // Start monitoring the test server (in a separate thread) to
+ // ensure we don't block on the child process more than a timeout.
+ startTestServerMonitor();
+
+ char msg[2];
+ GIOChannel* ioChannel = g_io_channel_unix_new(childStdout);
+ if (g_io_channel_read_chars(ioChannel, msg, 2, 0, 0) == G_IO_STATUS_NORMAL) {
+ // Check whether the server sent a message saying it's ready
+ // and store the result globally, so the monitor can see it.
+ kChildIsReady = msg[0] == 'O' && msg[1] == 'K';
+ }
+ g_io_channel_unref(ioChannel);
+ close(childStdout);
+
+ // The timeout was reached and the server is not ready yet, so
+ // stop it inmediately, and let the unit tests fail.
+ if (!kChildIsReady)
+ stopTestServer();
+}
+
+static void checkAtspiAccessible(AtspiAccessible* accessible, const char* targetName, AtspiRole targetRole)
+{
+ g_assert(ATSPI_IS_ACCESSIBLE(accessible));
+
+ GUniquePtr<char> name(atspi_accessible_get_name(accessible, 0));
+ g_assert_cmpstr(targetName, ==, name.get());
+ g_assert_cmpint(targetRole, ==, atspi_accessible_get_role(accessible, 0));
+}
+
+static GRefPtr<AtspiAccessible> findTestServerApplication()
+{
+ // Only one desktop is supported by ATSPI at the moment.
+ GRefPtr<AtspiAccessible> desktop = adoptGRef(atspi_get_desktop(0));
+
+ // Look for the server application in the list of apps.
+ GRefPtr<AtspiAccessible> current;
+ int childCount = atspi_accessible_get_child_count(desktop.get(), 0);
+ for (int i = 0; i < childCount; i++) {
+ current = adoptGRef(atspi_accessible_get_child_at_index(desktop.get(), i, 0));
+ if (!g_strcmp0(atspi_accessible_get_name(current.get(), 0), kTestServerAppName))
+ return current;
+ }
+
+ return 0;
+}
+
+static void testAtspiBasicHierarchy(WebViewTest* test, gconstpointer)
+{
+ // The test server's accessibility object (UI Process).
+ GRefPtr<AtspiAccessible> testServerApp = findTestServerApplication();
+ g_assert(ATSPI_IS_ACCESSIBLE(testServerApp.get()));
+ checkAtspiAccessible(testServerApp.get(), "AccessibilityTestServer", ATSPI_ROLE_APPLICATION);
+
+ // The main window's accessibility object (UI Process).
+ GRefPtr<AtspiAccessible> currentParent = testServerApp;
+ GRefPtr<AtspiAccessible> currentChild = adoptGRef(atspi_accessible_get_child_at_index(currentParent.get(), 0, 0));
+ g_assert(ATSPI_IS_ACCESSIBLE(currentChild.get()));
+ checkAtspiAccessible(currentChild.get(), "", ATSPI_ROLE_FRAME);
+
+ // The WebView's accessibility object (UI Process).
+ currentParent = currentChild;
+ currentChild = atspi_accessible_get_child_at_index(currentParent.get(), 0, 0);
+ g_assert(ATSPI_IS_ACCESSIBLE(currentChild.get()));
+ checkAtspiAccessible(currentChild.get(), "", ATSPI_ROLE_FILLER);
+
+ // The WebPage's accessibility object (Web Process).
+ currentParent = currentChild;
+ currentChild = atspi_accessible_get_child_at_index(currentParent.get(), 0, 0);
+ g_assert(ATSPI_IS_ACCESSIBLE(currentChild.get()));
+ checkAtspiAccessible(currentChild.get(), "", ATSPI_ROLE_FILLER);
+
+ // HTML root element's accessible element (Web Process).
+ currentParent = currentChild;
+ currentChild = atspi_accessible_get_child_at_index(currentParent.get(), 0, 0);
+ g_assert(ATSPI_IS_ACCESSIBLE(currentChild.get()));
+
+ // HTML body's accessible element (Web Process).
+ currentParent = currentChild;
+ currentChild = atspi_accessible_get_child_at_index(currentParent.get(), 0, 0);
+ g_assert(ATSPI_IS_ACCESSIBLE(currentChild.get()));
+ checkAtspiAccessible(currentChild.get(), "", ATSPI_ROLE_DOCUMENT_WEB);
+
+ // HTML H1's accessible element (Web Process).
+ currentParent = currentChild;
+ currentChild = atspi_accessible_get_child_at_index(currentParent.get(), 0, 0);
+ g_assert(ATSPI_IS_ACCESSIBLE(currentChild.get()));
+ checkAtspiAccessible(currentChild.get(), "This is a test", ATSPI_ROLE_HEADING);
+
+ // HTML first paragraph's accessible element (Web Process).
+ currentChild = atspi_accessible_get_child_at_index(currentParent.get(), 1, 0);
+ g_assert(ATSPI_IS_ACCESSIBLE(currentChild.get()));
+ checkAtspiAccessible(currentChild.get(), "", ATSPI_ROLE_PARAGRAPH);
+
+ // HTML second paragraph's accessible element (Web Process).
+ currentChild = atspi_accessible_get_child_at_index(currentParent.get(), 2, 0);
+ g_assert(ATSPI_IS_ACCESSIBLE(currentChild.get()));
+ checkAtspiAccessible(currentChild.get(), "", ATSPI_ROLE_PARAGRAPH);
+
+ // HTML link's accessible element (Web Process).
+ currentParent = currentChild;
+ currentChild = atspi_accessible_get_child_at_index(currentParent.get(), 0, 0);
+ g_assert(ATSPI_IS_ACCESSIBLE(currentChild.get()));
+ checkAtspiAccessible(currentChild.get(), "a link", ATSPI_ROLE_LINK);
+}
+
+void beforeAll()
+{
+ // We install a handler to ensure that we kill the child process
+ // if the parent dies because of whatever the reason is.
+ signal(SIGABRT, sigAbortHandler);
+
+ // Start the accessibility test server and load the tests.
+ startTestServer();
+ WebViewTest::add("WebKitAccessibility", "atspi-basic-hierarchy", testAtspiBasicHierarchy);
+}
+
+void afterAll()
+{
+ // Ensure we stop the server.
+ stopTestServer();
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFaviconDatabase.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFaviconDatabase.cpp
new file mode 100644
index 000000000..8d5be9250
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFaviconDatabase.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebKitTestServer.h"
+#include "WebViewTest.h"
+#include <glib/gstdio.h>
+#include <libsoup/soup.h>
+#include <wtf/glib/GUniquePtr.h>
+
+static WebKitTestServer* kServer;
+
+class FaviconDatabaseTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(FaviconDatabaseTest);
+
+ FaviconDatabaseTest()
+ : m_favicon(nullptr)
+ , m_faviconNotificationReceived(false)
+ {
+ WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(m_webContext.get());
+ g_signal_connect(database, "favicon-changed", G_CALLBACK(faviconChangedCallback), this);
+ }
+
+ ~FaviconDatabaseTest()
+ {
+ if (m_favicon)
+ cairo_surface_destroy(m_favicon);
+
+ WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(m_webContext.get());
+ g_signal_handlers_disconnect_matched(database, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ static void faviconChangedCallback(WebKitFaviconDatabase* database, const char* pageURI, const char* faviconURI, FaviconDatabaseTest* test)
+ {
+ if (!g_strcmp0(webkit_web_view_get_uri(test->m_webView), pageURI))
+ test->m_faviconURI = faviconURI;
+ }
+
+ static void viewFaviconChangedCallback(WebKitWebView* webView, GParamSpec* pspec, gpointer data)
+ {
+ FaviconDatabaseTest* test = static_cast<FaviconDatabaseTest*>(data);
+ g_assert(test->m_webView == webView);
+ test->m_faviconNotificationReceived = true;
+ test->quitMainLoop();
+ }
+
+ static void getFaviconCallback(GObject* sourceObject, GAsyncResult* result, void* data)
+ {
+ FaviconDatabaseTest* test = static_cast<FaviconDatabaseTest*>(data);
+ WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(test->m_webContext.get());
+ test->m_favicon = webkit_favicon_database_get_favicon_finish(database, result, &test->m_error.outPtr());
+ test->quitMainLoop();
+ }
+
+ void waitUntilFaviconChanged()
+ {
+ m_faviconNotificationReceived = false;
+ unsigned long handlerID = g_signal_connect(m_webView, "notify::favicon", G_CALLBACK(viewFaviconChangedCallback), this);
+ g_main_loop_run(m_mainLoop);
+ g_signal_handler_disconnect(m_webView, handlerID);
+ }
+
+ void getFaviconForPageURIAndWaitUntilReady(const char* pageURI)
+ {
+ if (m_favicon) {
+ cairo_surface_destroy(m_favicon);
+ m_favicon = 0;
+ }
+
+ WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(m_webContext.get());
+ webkit_favicon_database_get_favicon(database, pageURI, 0, getFaviconCallback, this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ cairo_surface_t* m_favicon;
+ CString m_faviconURI;
+ GUniqueOutPtr<GError> m_error;
+ bool m_faviconNotificationReceived;
+};
+
+static void
+serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable* query, SoupClientContext* context, void* data)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (g_str_equal(path, "/favicon.ico")) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+ soup_message_body_complete(message->response_body);
+ return;
+ }
+
+ char* contents;
+ gsize length;
+ if (g_str_equal(path, "/icon/favicon.ico")) {
+ GUniquePtr<char> pathToFavicon(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr));
+ g_file_get_contents(pathToFavicon.get(), &contents, &length, 0);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, length);
+ } else if (g_str_equal(path, "/nofavicon")) {
+ static const char* noFaviconHTML = "<html><head><body>test</body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, noFaviconHTML, strlen(noFaviconHTML));
+ } else {
+ static const char* contentsHTML = "<html><head><link rel='icon' href='/icon/favicon.ico' type='image/x-ico; charset=binary'></head><body>test</body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, contentsHTML, strlen(contentsHTML));
+ }
+
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_complete(message->response_body);
+}
+
+static void testNotInitialized(FaviconDatabaseTest* test)
+{
+ // Try to retrieve a valid favicon from a not initialized database.
+ test->getFaviconForPageURIAndWaitUntilReady(kServer->getURIForPath("/foo").data());
+ g_assert(!test->m_favicon);
+ g_assert(test->m_error);
+ g_assert_cmpint(test->m_error->code, ==, WEBKIT_FAVICON_DATABASE_ERROR_NOT_INITIALIZED);
+}
+
+static void testSetDirectory(FaviconDatabaseTest* test)
+{
+ webkit_web_context_set_favicon_database_directory(test->m_webContext.get(), Test::dataDirectory());
+ g_assert_cmpstr(Test::dataDirectory(), ==, webkit_web_context_get_favicon_database_directory(test->m_webContext.get()));
+}
+
+static void testClearDatabase(FaviconDatabaseTest* test)
+{
+ WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(test->m_webContext.get());
+ webkit_favicon_database_clear(database);
+
+ GUniquePtr<char> iconURI(webkit_favicon_database_get_favicon_uri(database, kServer->getURIForPath("/foo").data()));
+ g_assert(!iconURI);
+}
+
+static void testGetFavicon(FaviconDatabaseTest* test)
+{
+ // We need to load the page first to ensure the icon data will be
+ // in the database in case there's an associated favicon.
+ test->loadURI(kServer->getURIForPath("/foo").data());
+ test->waitUntilFaviconChanged();
+ CString faviconURI = kServer->getURIForPath("/icon/favicon.ico");
+
+ // Check the API retrieving a valid favicon.
+ test->getFaviconForPageURIAndWaitUntilReady(kServer->getURIForPath("/foo").data());
+ g_assert(test->m_favicon);
+ g_assert_cmpstr(test->m_faviconURI.data(), ==, faviconURI.data());
+ g_assert(!test->m_error);
+
+ // Check that width and height match those from blank.ico (16x16 favicon).
+ g_assert_cmpint(cairo_image_surface_get_width(test->m_favicon), ==, 16);
+ g_assert_cmpint(cairo_image_surface_get_height(test->m_favicon), ==, 16);
+
+ // Check that another page with the same favicon return the same icon.
+ cairo_surface_t* favicon = cairo_surface_reference(test->m_favicon);
+ test->loadURI(kServer->getURIForPath("/bar").data());
+ // It's a new page in the database, so favicon will change twice, first to reset it
+ // and then when the icon is loaded.
+ test->waitUntilFaviconChanged();
+ test->waitUntilFaviconChanged();
+ test->getFaviconForPageURIAndWaitUntilReady(kServer->getURIForPath("/bar").data());
+ g_assert(test->m_favicon);
+ g_assert_cmpstr(test->m_faviconURI.data(), ==, faviconURI.data());
+ g_assert(test->m_favicon == favicon);
+ g_assert(!test->m_error);
+ cairo_surface_destroy(favicon);
+
+ // Check the API retrieving an invalid favicon.
+ test->loadURI(kServer->getURIForPath("/nofavicon").data());
+ test->waitUntilFaviconChanged();
+
+ test->getFaviconForPageURIAndWaitUntilReady(kServer->getURIForPath("/nofavicon").data());
+ g_assert(!test->m_favicon);
+ g_assert(test->m_error);
+}
+
+static void testGetFaviconURI(FaviconDatabaseTest* test)
+{
+ WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(test->m_webContext.get());
+
+ CString baseURI = kServer->getURIForPath("/foo");
+ GUniquePtr<char> iconURI(webkit_favicon_database_get_favicon_uri(database, baseURI.data()));
+ ASSERT_CMP_CSTRING(iconURI.get(), ==, kServer->getURIForPath("/icon/favicon.ico"));
+}
+
+static void testWebViewFavicon(FaviconDatabaseTest* test)
+{
+ test->m_faviconURI = CString();
+
+ cairo_surface_t* iconFromWebView = webkit_web_view_get_favicon(test->m_webView);
+ g_assert(!iconFromWebView);
+
+ test->loadURI(kServer->getURIForPath("/foo").data());
+ test->waitUntilFaviconChanged();
+ g_assert(test->m_faviconNotificationReceived);
+ // The icon is known and hasn't changed in the database, so notify::favicon is emitted
+ // but WebKitFaviconDatabase::icon-changed isn't.
+ g_assert(test->m_faviconURI.isNull());
+
+ iconFromWebView = webkit_web_view_get_favicon(test->m_webView);
+ g_assert(iconFromWebView);
+ g_assert_cmpuint(cairo_image_surface_get_width(iconFromWebView), ==, 16);
+ g_assert_cmpuint(cairo_image_surface_get_height(iconFromWebView), ==, 16);
+}
+
+static void testFaviconDatabase(FaviconDatabaseTest* test, gconstpointer)
+{
+ // These tests depend on this order to run properly so we declare them in a single one.
+ // See https://bugs.webkit.org/show_bug.cgi?id=111434.
+ testNotInitialized(test);
+ testSetDirectory(test);
+ testGetFavicon(test);
+ testGetFaviconURI(test);
+ testWebViewFavicon(test);
+ testClearDatabase(test);
+}
+
+void beforeAll()
+{
+ // Start a soup server for testing.
+ kServer = new WebKitTestServer();
+ kServer->run(serverCallback);
+
+ // Add tests to the suite.
+ FaviconDatabaseTest::add("WebKitFaviconDatabase", "favicon-database-test", testFaviconDatabase);
+}
+
+void afterAll()
+{
+ delete kServer;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFindController.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFindController.cpp
new file mode 100644
index 000000000..7289fd37f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFindController.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "LoadTrackingTest.h"
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+#include <wtf/glib/GRefPtr.h>
+
+static const char* testString = "<html><body>first testing second testing secondHalf</body></html>";
+
+class FindControllerTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(FindControllerTest);
+
+ FindControllerTest()
+ : m_findController(webkit_web_view_get_find_controller(m_webView))
+ , m_runFindUntilCompletion(false)
+ {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_findController.get()));
+ }
+
+ ~FindControllerTest()
+ {
+ if (m_findController)
+ g_signal_handlers_disconnect_matched(m_findController.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ void find(const char* searchText, guint32 findOptions, guint maxMatchCount)
+ {
+ g_signal_connect(m_findController.get(), "found-text", G_CALLBACK(foundTextCallback), this);
+ g_signal_connect(m_findController.get(), "failed-to-find-text", G_CALLBACK(failedToFindTextCallback), this);
+ webkit_find_controller_search(m_findController.get(), searchText, findOptions, maxMatchCount);
+ }
+
+ void count(const char* searchText, guint32 findOptions, guint maxMatchCount)
+ {
+ g_signal_connect(m_findController.get(), "counted-matches", G_CALLBACK(countedMatchesCallback), this);
+ webkit_find_controller_count_matches(m_findController.get(), searchText, findOptions, maxMatchCount);
+ }
+
+ void waitUntilFindFinished()
+ {
+ m_runFindUntilCompletion = true;
+ g_main_loop_run(m_mainLoop);
+ }
+
+ GRefPtr<WebKitFindController> m_findController;
+ bool m_textFound;
+ unsigned m_matchCount;
+
+private:
+ bool m_runFindUntilCompletion;
+
+ static void foundTextCallback(WebKitFindController*, guint matchCount, FindControllerTest* test)
+ {
+ test->m_textFound = true;
+ test->m_matchCount = matchCount;
+ if (test->m_runFindUntilCompletion)
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ static void failedToFindTextCallback(WebKitFindController*, FindControllerTest* test)
+ {
+ test->m_textFound = false;
+ if (test->m_runFindUntilCompletion)
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ static void countedMatchesCallback(WebKitFindController*, guint matchCount, FindControllerTest* test)
+ {
+ test->m_matchCount = matchCount;
+ if (test->m_runFindUntilCompletion)
+ g_main_loop_quit(test->m_mainLoop);
+ }
+};
+
+static void testFindControllerTextFound(FindControllerTest* test, gconstpointer)
+{
+ test->loadHtml(testString, 0);
+ test->waitUntilLoadFinished();
+
+ test->find("testing", WEBKIT_FIND_OPTIONS_NONE, 1);
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_textFound);
+}
+
+static void testFindControllerTextNotFound(FindControllerTest* test, gconstpointer)
+{
+ test->loadHtml(testString, 0);
+ test->waitUntilLoadFinished();
+
+ test->find("notFound", WEBKIT_FIND_OPTIONS_NONE, 1);
+ test->waitUntilFindFinished();
+
+ g_assert(!test->m_textFound);
+}
+
+static void testFindControllerMatchCount(FindControllerTest* test, gconstpointer)
+{
+ test->loadHtml(testString, 0);
+ test->waitUntilLoadFinished();
+
+ test->find("testing", WEBKIT_FIND_OPTIONS_NONE, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_matchCount == 2);
+ g_assert(test->m_textFound);
+}
+
+static void testFindControllerMaxMatchCount(FindControllerTest* test, gconstpointer)
+{
+ test->loadHtml(testString, 0);
+ test->waitUntilLoadFinished();
+
+ test->find("testing", WEBKIT_FIND_OPTIONS_NONE, 1);
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_matchCount == G_MAXUINT);
+ g_assert(test->m_textFound);
+}
+
+static void testFindControllerNext(FindControllerTest* test, gconstpointer)
+{
+ test->loadHtml(testString, 0);
+ test->waitUntilLoadFinished();
+
+ test->find("testing", WEBKIT_FIND_OPTIONS_NONE, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_textFound);
+ g_assert(test->m_matchCount == 2);
+
+ webkit_find_controller_search_next(test->m_findController.get());
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_textFound);
+ g_assert(test->m_matchCount == 1);
+ g_assert(!(webkit_find_controller_get_options(test->m_findController.get()) & WEBKIT_FIND_OPTIONS_BACKWARDS));
+
+ webkit_find_controller_search_next(test->m_findController.get());
+ test->waitUntilFindFinished();
+
+ g_assert(!test->m_textFound);
+ g_assert(test->m_matchCount == 1);
+ g_assert(!(webkit_find_controller_get_options(test->m_findController.get()) & WEBKIT_FIND_OPTIONS_BACKWARDS));
+}
+
+static void testFindControllerPrevious(FindControllerTest* test, gconstpointer)
+{
+ test->loadHtml(testString, 0);
+ test->waitUntilLoadFinished();
+
+ test->find("testing", WEBKIT_FIND_OPTIONS_NONE, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_matchCount == 2);
+ g_assert(test->m_textFound);
+
+ webkit_find_controller_search_next(test->m_findController.get());
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_textFound);
+ g_assert(test->m_matchCount == 1);
+ g_assert(!(webkit_find_controller_get_options(test->m_findController.get()) & WEBKIT_FIND_OPTIONS_BACKWARDS));
+
+ webkit_find_controller_search_previous(test->m_findController.get());
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_textFound);
+ g_assert(test->m_matchCount == 1);
+ g_assert(webkit_find_controller_get_options(test->m_findController.get()) & WEBKIT_FIND_OPTIONS_BACKWARDS);
+}
+
+static void testFindControllerCountedMatches(FindControllerTest* test, gconstpointer)
+{
+ test->loadHtml(testString, 0);
+ test->waitUntilLoadFinished();
+
+ test->count("testing", WEBKIT_FIND_OPTIONS_NONE, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_matchCount == 2);
+
+ test->count("first", WEBKIT_FIND_OPTIONS_NONE, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_matchCount == 1);
+
+ test->count("notFound", WEBKIT_FIND_OPTIONS_NONE, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(!test->m_matchCount);
+}
+
+static void testFindControllerOptions(FindControllerTest* test, gconstpointer)
+{
+ test->loadHtml(testString, 0);
+ test->waitUntilLoadFinished();
+
+ test->find("Testing", WEBKIT_FIND_OPTIONS_NONE, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(!test->m_textFound);
+
+ test->find("Testing", WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_textFound);
+
+ test->find("esting", WEBKIT_FIND_OPTIONS_NONE, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_textFound);
+
+ test->find("esting", WEBKIT_FIND_OPTIONS_AT_WORD_STARTS, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(!test->m_textFound);
+
+ test->find("Half", WEBKIT_FIND_OPTIONS_AT_WORD_STARTS, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(!test->m_textFound);
+
+ test->find("Half", WEBKIT_FIND_OPTIONS_AT_WORD_STARTS | WEBKIT_FIND_OPTIONS_TREAT_MEDIAL_CAPITAL_AS_WORD_START, 2);
+ test->waitUntilFindFinished();
+
+ g_assert(test->m_textFound);
+
+ test->find("testing", WEBKIT_FIND_OPTIONS_WRAP_AROUND, 3);
+ test->waitUntilFindFinished();
+ g_assert(test->m_textFound);
+
+ webkit_find_controller_search_next(test->m_findController.get());
+ test->waitUntilFindFinished();
+ g_assert(test->m_textFound);
+
+ webkit_find_controller_search_next(test->m_findController.get());
+ test->waitUntilFindFinished();
+ g_assert(test->m_textFound);
+}
+
+static void testFindControllerHide(FindControllerTest* test, gconstpointer)
+{
+ test->loadHtml(testString, 0);
+ test->waitUntilLoadFinished();
+
+ test->showInWindowAndWaitUntilMapped();
+
+ cairo_surface_t* originalSurface = cairo_surface_reference(
+ test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT, WEBKIT_SNAPSHOT_OPTIONS_NONE));
+ g_assert(originalSurface);
+
+ test->find("testing", WEBKIT_FIND_OPTIONS_NONE, 1);
+ test->waitUntilFindFinished();
+ g_assert(test->m_textFound);
+
+ cairo_surface_t* highlightSurface = cairo_surface_reference(
+ test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT, WEBKIT_SNAPSHOT_OPTIONS_NONE));
+ g_assert(highlightSurface);
+ g_assert(!Test::cairoSurfacesEqual(originalSurface, highlightSurface));
+
+ WebKitFindController* findController = webkit_web_view_get_find_controller(test->m_webView);
+ webkit_find_controller_search_finish(findController);
+ webkit_web_view_execute_editing_command(test->m_webView, "Unselect");
+
+ cairo_surface_t* unhighlightSurface = cairo_surface_reference(
+ test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT, WEBKIT_SNAPSHOT_OPTIONS_NONE));
+ g_assert(unhighlightSurface);
+ g_assert(Test::cairoSurfacesEqual(originalSurface, unhighlightSurface));
+
+ cairo_surface_destroy(originalSurface);
+ cairo_surface_destroy(highlightSurface);
+ cairo_surface_destroy(unhighlightSurface);
+}
+
+static void testFindControllerInstance(FindControllerTest* test, gconstpointer)
+{
+ WebKitFindController* findController1 = webkit_web_view_get_find_controller(test->m_webView);
+ WebKitFindController* findController2 = webkit_web_view_get_find_controller(test->m_webView);
+
+ g_assert(findController1 == findController2);
+}
+
+static void testFindControllerGetters(FindControllerTest* test, gconstpointer)
+{
+ const char* searchText = "testing";
+ guint maxMatchCount = 1;
+ guint32 findOptions = WEBKIT_FIND_OPTIONS_WRAP_AROUND | WEBKIT_FIND_OPTIONS_AT_WORD_STARTS;
+ WebKitFindController* findController = webkit_web_view_get_find_controller(test->m_webView);
+
+ webkit_find_controller_search(findController, searchText, findOptions, maxMatchCount);
+ g_assert(webkit_find_controller_get_web_view(findController) == test->m_webView);
+ g_assert(!g_strcmp0(webkit_find_controller_get_search_text(findController), searchText));
+ g_assert(webkit_find_controller_get_max_match_count(findController) == maxMatchCount);
+ g_assert(webkit_find_controller_get_options(findController) == findOptions);
+}
+
+void beforeAll()
+{
+ FindControllerTest::add("WebKitFindController", "getters", testFindControllerGetters);
+ FindControllerTest::add("WebKitFindController", "instance", testFindControllerInstance);
+ FindControllerTest::add("WebKitFindController", "text-found", testFindControllerTextFound);
+ FindControllerTest::add("WebKitFindController", "text-not-found", testFindControllerTextNotFound);
+ FindControllerTest::add("WebKitFindController", "match-count", testFindControllerMatchCount);
+ FindControllerTest::add("WebKitFindController", "max-match-count", testFindControllerMaxMatchCount);
+ FindControllerTest::add("WebKitFindController", "next", testFindControllerNext);
+ FindControllerTest::add("WebKitFindController", "previous", testFindControllerPrevious);
+ FindControllerTest::add("WebKitFindController", "counted-matches", testFindControllerCountedMatches);
+ FindControllerTest::add("WebKitFindController", "options", testFindControllerOptions);
+ FindControllerTest::add("WebKitFindController", "hide", testFindControllerHide);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitPolicyClient.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitPolicyClient.cpp
new file mode 100644
index 000000000..9f9515123
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitPolicyClient.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "LoadTrackingTest.h"
+#include "WebKitTestServer.h"
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/text/CString.h>
+
+static WebKitTestServer* kServer;
+
+class PolicyClientTest: public LoadTrackingTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(PolicyClientTest);
+
+ enum PolicyDecisionResponse {
+ Use,
+ Ignore,
+ Download,
+ None
+ };
+
+ PolicyClientTest()
+ : LoadTrackingTest()
+ , m_policyDecisionResponse(None)
+ , m_policyDecisionTypeFilter(0)
+ , m_respondToPolicyDecisionAsynchronously(false)
+ , m_haltMainLoopAfterMakingDecision(false)
+ {
+ g_signal_connect(m_webView, "decide-policy", G_CALLBACK(decidePolicyCallback), this);
+ }
+
+ static gboolean quitMainLoopLater(GMainLoop* loop)
+ {
+ g_main_loop_quit(loop);
+ return FALSE;
+ }
+
+ static void respondToPolicyDecision(PolicyClientTest* test, WebKitPolicyDecision* decision)
+ {
+ switch (test->m_policyDecisionResponse) {
+ case Use:
+ webkit_policy_decision_use(decision);
+ break;
+ case Ignore:
+ webkit_policy_decision_ignore(decision);
+ break;
+ case Download:
+ webkit_policy_decision_download(decision);
+ break;
+ case None:
+ break;
+ }
+
+ if (test->m_haltMainLoopAfterMakingDecision)
+ g_idle_add(reinterpret_cast<GSourceFunc>(quitMainLoopLater), test->m_mainLoop);
+ }
+
+ static gboolean respondToPolicyDecisionLater(PolicyClientTest* test)
+ {
+ respondToPolicyDecision(test, test->m_previousPolicyDecision.get());
+ test->m_previousPolicyDecision = 0;
+ return FALSE;
+ }
+
+ static gboolean decidePolicyCallback(WebKitWebView* webView, WebKitPolicyDecision* decision, WebKitPolicyDecisionType type, PolicyClientTest* test)
+ {
+ if (test->m_policyDecisionTypeFilter != type)
+ return FALSE;
+
+ test->m_previousPolicyDecision = decision;
+ if (test->m_respondToPolicyDecisionAsynchronously) {
+ g_idle_add(reinterpret_cast<GSourceFunc>(respondToPolicyDecisionLater), test);
+ return TRUE;
+ }
+
+ respondToPolicyDecision(test, decision);
+
+ // We return FALSE here to ensure that the default policy decision
+ // handler doesn't override whatever we use here.
+ return FALSE;
+ }
+
+ PolicyDecisionResponse m_policyDecisionResponse;
+ int m_policyDecisionTypeFilter;
+ bool m_respondToPolicyDecisionAsynchronously;
+ bool m_haltMainLoopAfterMakingDecision;
+ GRefPtr<WebKitPolicyDecision> m_previousPolicyDecision;
+};
+
+static void testNavigationPolicy(PolicyClientTest* test, gconstpointer)
+{
+ test->m_policyDecisionTypeFilter = WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION;
+
+ test->m_policyDecisionResponse = PolicyClientTest::Use;
+ test->loadHtml("<html/>", "http://webkitgtk.org/");
+ test->waitUntilLoadFinished();
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+
+ // Ideally we'd like to have a more intensive test here, but it's still pretty tricky
+ // to trigger different types of navigations with the GTK+ WebKit2 API.
+ WebKitNavigationPolicyDecision* decision = WEBKIT_NAVIGATION_POLICY_DECISION(test->m_previousPolicyDecision.get());
+ WebKitNavigationAction* navigationAction = webkit_navigation_policy_decision_get_navigation_action(decision);
+ g_assert_cmpint(webkit_navigation_action_get_navigation_type(navigationAction), ==, WEBKIT_NAVIGATION_TYPE_OTHER);
+ g_assert_cmpint(webkit_navigation_action_get_mouse_button(navigationAction), ==, 0);
+ g_assert_cmpint(webkit_navigation_action_get_modifiers(navigationAction), ==, 0);
+ g_assert(!webkit_navigation_policy_decision_get_frame_name(decision));
+ WebKitURIRequest* request = webkit_navigation_action_get_request(navigationAction);
+ g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, "http://webkitgtk.org/");
+
+ test->m_policyDecisionResponse = PolicyClientTest::Use;
+ test->m_respondToPolicyDecisionAsynchronously = true;
+ test->loadHtml("<html/>", "http://webkitgtk.org/");
+ test->waitUntilLoadFinished();
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+
+ // If we are waiting until load completion, it will never complete if we ignore the
+ // navigation. So we tell the main loop to quit sometime later.
+ test->m_policyDecisionResponse = PolicyClientTest::Ignore;
+ test->m_respondToPolicyDecisionAsynchronously = false;
+ test->m_haltMainLoopAfterMakingDecision = true;
+ test->loadHtml("<html/>", "http://webkitgtk.org/");
+ test->waitUntilLoadFinished();
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 0);
+
+ test->m_policyDecisionResponse = PolicyClientTest::Ignore;
+ test->loadHtml("<html/>", "http://webkitgtk.org/");
+ test->waitUntilLoadFinished();
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 0);
+}
+
+static void testResponsePolicy(PolicyClientTest* test, gconstpointer)
+{
+ test->m_policyDecisionTypeFilter = WEBKIT_POLICY_DECISION_TYPE_RESPONSE;
+
+ test->m_policyDecisionResponse = PolicyClientTest::Use;
+ test->loadURI(kServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+
+ WebKitResponsePolicyDecision* decision = WEBKIT_RESPONSE_POLICY_DECISION(test->m_previousPolicyDecision.get());
+ WebKitURIRequest* request = webkit_response_policy_decision_get_request(decision);
+ g_assert(WEBKIT_IS_URI_REQUEST(request));
+ ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/"));
+ WebKitURIResponse* response = webkit_response_policy_decision_get_response(decision);
+ g_assert(WEBKIT_IS_URI_RESPONSE(response));
+ ASSERT_CMP_CSTRING(webkit_uri_response_get_uri(response), ==, kServer->getURIForPath("/"));
+ g_assert(webkit_web_view_can_show_mime_type(test->m_webView, webkit_uri_response_get_mime_type(response)) ==
+ webkit_response_policy_decision_is_mime_type_supported(decision));
+
+ test->m_respondToPolicyDecisionAsynchronously = true;
+ test->loadURI(kServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+
+ test->m_respondToPolicyDecisionAsynchronously = false;
+ test->m_policyDecisionResponse = PolicyClientTest::Ignore;
+ test->loadURI(kServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+
+ g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
+ g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
+ g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::ProvisionalLoadFailed);
+ g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
+}
+
+struct CreateCallbackData {
+ bool triedToOpenWindow;
+ GMainLoop* mainLoop;
+};
+
+static WebKitWebView* createCallback(WebKitWebView* webView, WebKitNavigationAction*, CreateCallbackData* data)
+{
+ data->triedToOpenWindow = true;
+ g_main_loop_quit(data->mainLoop);
+ return 0;
+}
+
+static void testNewWindowPolicy(PolicyClientTest* test, gconstpointer)
+{
+ static const char* windowOpeningHTML =
+ "<html><body>"
+ " <a id=\"link\" href=\"http://www.google.com\" target=\"_blank\">Link</a>"
+ " <script>"
+ " var event = document.createEvent('MouseEvents');"
+ " event.initEvent('click', true, false);"
+ " document.getElementById('link').dispatchEvent(event);"
+ " </script>"
+ "</body></html>";
+ test->m_policyDecisionTypeFilter = WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION;
+ webkit_settings_set_javascript_can_open_windows_automatically(webkit_web_view_get_settings(test->m_webView), TRUE);
+
+ CreateCallbackData data;
+ data.triedToOpenWindow = false;
+ data.mainLoop = test->m_mainLoop;
+
+ g_signal_connect(test->m_webView, "create", G_CALLBACK(createCallback), &data);
+ test->m_policyDecisionResponse = PolicyClientTest::Use;
+ test->loadHtml(windowOpeningHTML, "http://webkitgtk.org/");
+ test->wait(1);
+ g_assert(data.triedToOpenWindow);
+
+ WebKitNavigationPolicyDecision* decision = WEBKIT_NAVIGATION_POLICY_DECISION(test->m_previousPolicyDecision.get());
+ g_assert_cmpstr(webkit_navigation_policy_decision_get_frame_name(decision), ==, "_blank");
+
+ // Using a short timeout is a bit ugly here, but it's hard to get around because if we block
+ // the new window signal we cannot halt the main loop in the create callback. If we
+ // halt the main loop in the policy decision, the create callback never executes.
+ data.triedToOpenWindow = false;
+ test->m_policyDecisionResponse = PolicyClientTest::Ignore;
+ test->loadHtml(windowOpeningHTML, "http://webkitgtk.org/");
+ test->wait(.2);
+ g_assert(!data.triedToOpenWindow);
+}
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (g_str_equal(path, "/")) {
+ static const char* responseString = "<html><body>Testing!</body></html>";
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, responseString, strlen(responseString));
+ soup_message_body_complete(message->response_body);
+ } else
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+}
+
+void beforeAll()
+{
+ kServer = new WebKitTestServer();
+ kServer->run(serverCallback);
+
+ PolicyClientTest::add("WebKitPolicyClient", "navigation-policy", testNavigationPolicy);
+ PolicyClientTest::add("WebKitPolicyClient", "response-policy", testResponsePolicy);
+ PolicyClientTest::add("WebKitPolicyClient", "new-window-policy", testNewWindowPolicy);
+}
+
+void afterAll()
+{
+ delete kServer;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSettings.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSettings.cpp
new file mode 100644
index 000000000..b3a377270
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSettings.cpp
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * Neither the name of Motorola Mobility, Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "TestMain.h"
+#include "WebViewTest.h"
+#include "WebKitTestServer.h"
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+#include <wtf/glib/GRefPtr.h>
+
+static WebKitTestServer* gServer;
+
+static void testWebKitSettings(Test*, gconstpointer)
+{
+ WebKitSettings* settings = webkit_settings_new();
+
+ // JavaScript is enabled by default.
+ g_assert(webkit_settings_get_enable_javascript(settings));
+ webkit_settings_set_enable_javascript(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_javascript(settings));
+
+ // By default auto-load-image is true.
+ g_assert(webkit_settings_get_auto_load_images(settings));
+ webkit_settings_set_auto_load_images(settings, FALSE);
+ g_assert(!webkit_settings_get_auto_load_images(settings));
+
+ // load-icons-ignoring-image-load-setting is false by default.
+ g_assert(!webkit_settings_get_load_icons_ignoring_image_load_setting(settings));
+ webkit_settings_set_load_icons_ignoring_image_load_setting(settings, TRUE);
+ g_assert(webkit_settings_get_load_icons_ignoring_image_load_setting(settings));
+
+ // Offline application cache is true by default.
+ g_assert(webkit_settings_get_enable_offline_web_application_cache(settings));
+ webkit_settings_set_enable_offline_web_application_cache(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_offline_web_application_cache(settings));
+
+ // Local storage is enable by default.
+ g_assert(webkit_settings_get_enable_html5_local_storage(settings));
+ webkit_settings_set_enable_html5_local_storage(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_html5_local_storage(settings));
+
+ // HTML5 database is enabled by default.
+ g_assert(webkit_settings_get_enable_html5_database(settings));
+ webkit_settings_set_enable_html5_database(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_html5_database(settings));
+
+ // XSS Auditor is enabled by default.
+ g_assert(webkit_settings_get_enable_xss_auditor(settings));
+ webkit_settings_set_enable_xss_auditor(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_xss_auditor(settings));
+
+ // Frame flattening is disabled by default.
+ g_assert(!webkit_settings_get_enable_frame_flattening(settings));
+ webkit_settings_set_enable_frame_flattening(settings, TRUE);
+ g_assert(webkit_settings_get_enable_frame_flattening(settings));
+
+ // Plugins are enabled by default.
+ g_assert(webkit_settings_get_enable_plugins(settings));
+ webkit_settings_set_enable_plugins(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_plugins(settings));
+
+ // Java is enabled by default.
+ g_assert(webkit_settings_get_enable_java(settings));
+ webkit_settings_set_enable_java(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_java(settings));
+
+ // By default, JavaScript can open windows automatically is disabled.
+ g_assert(!webkit_settings_get_javascript_can_open_windows_automatically(settings));
+ webkit_settings_set_javascript_can_open_windows_automatically(settings, TRUE);
+ g_assert(webkit_settings_get_javascript_can_open_windows_automatically(settings));
+
+ // By default hyper link auditing is disabled.
+ g_assert(!webkit_settings_get_enable_hyperlink_auditing(settings));
+ webkit_settings_set_enable_hyperlink_auditing(settings, TRUE);
+ g_assert(webkit_settings_get_enable_hyperlink_auditing(settings));
+
+ // Default font family is "sans-serif".
+ g_assert_cmpstr(webkit_settings_get_default_font_family(settings), ==, "sans-serif");
+ webkit_settings_set_default_font_family(settings, "monospace");
+ g_assert_cmpstr(webkit_settings_get_default_font_family(settings), ==, "monospace");
+
+ // Default monospace font family font family is "monospace".
+ g_assert_cmpstr(webkit_settings_get_monospace_font_family(settings), ==, "monospace");
+ webkit_settings_set_monospace_font_family(settings, "sans-serif");
+ g_assert_cmpstr(webkit_settings_get_monospace_font_family(settings), ==, "sans-serif");
+
+ // Default serif font family is "serif".
+ g_assert_cmpstr(webkit_settings_get_serif_font_family(settings), ==, "serif");
+ webkit_settings_set_serif_font_family(settings, "sans-serif");
+ g_assert_cmpstr(webkit_settings_get_serif_font_family(settings), ==, "sans-serif");
+
+ // Default sans serif font family is "sans-serif".
+ g_assert_cmpstr(webkit_settings_get_sans_serif_font_family(settings), ==, "sans-serif");
+ webkit_settings_set_sans_serif_font_family(settings, "serif");
+ g_assert_cmpstr(webkit_settings_get_sans_serif_font_family(settings), ==, "serif");
+
+ // Default cursive font family "serif".
+ g_assert_cmpstr(webkit_settings_get_cursive_font_family(settings), ==, "serif");
+ webkit_settings_set_cursive_font_family(settings, "sans-serif");
+ g_assert_cmpstr(webkit_settings_get_cursive_font_family(settings), ==, "sans-serif");
+
+ // Default fantasy font family is "serif".
+ g_assert_cmpstr(webkit_settings_get_fantasy_font_family(settings), ==, "serif");
+ webkit_settings_set_fantasy_font_family(settings, "sans-serif");
+ g_assert_cmpstr(webkit_settings_get_fantasy_font_family(settings), ==, "sans-serif");
+
+ // Default pictograph font family is "serif".
+ g_assert_cmpstr(webkit_settings_get_pictograph_font_family(settings), ==, "serif");
+ webkit_settings_set_pictograph_font_family(settings, "sans-serif");
+ g_assert_cmpstr(webkit_settings_get_pictograph_font_family(settings), ==, "sans-serif");
+
+ // Default font size is 16.
+ g_assert_cmpuint(webkit_settings_get_default_font_size(settings), ==, 16);
+ webkit_settings_set_default_font_size(settings, 14);
+ g_assert_cmpuint(webkit_settings_get_default_font_size(settings), ==, 14);
+
+ // Default monospace font size is 13.
+ g_assert_cmpuint(webkit_settings_get_default_monospace_font_size(settings), ==, 13);
+ webkit_settings_set_default_monospace_font_size(settings, 10);
+ g_assert_cmpuint(webkit_settings_get_default_monospace_font_size(settings), ==, 10);
+
+ // Default minimum font size is 0.
+ g_assert_cmpuint(webkit_settings_get_minimum_font_size(settings), ==, 0);
+ webkit_settings_set_minimum_font_size(settings, 7);
+ g_assert_cmpuint(webkit_settings_get_minimum_font_size(settings), ==, 7);
+
+ // Default charset is "iso-8859-1".
+ g_assert_cmpstr(webkit_settings_get_default_charset(settings), ==, "iso-8859-1");
+ webkit_settings_set_default_charset(settings, "utf8");
+ g_assert_cmpstr(webkit_settings_get_default_charset(settings), ==, "utf8");
+
+ g_assert(!webkit_settings_get_enable_private_browsing(settings));
+ webkit_settings_set_enable_private_browsing(settings, TRUE);
+ g_assert(webkit_settings_get_enable_private_browsing(settings));
+
+ g_assert(!webkit_settings_get_enable_developer_extras(settings));
+ webkit_settings_set_enable_developer_extras(settings, TRUE);
+ g_assert(webkit_settings_get_enable_developer_extras(settings));
+
+ g_assert(webkit_settings_get_enable_resizable_text_areas(settings));
+ webkit_settings_set_enable_resizable_text_areas(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_resizable_text_areas(settings));
+
+ g_assert(webkit_settings_get_enable_tabs_to_links(settings));
+ webkit_settings_set_enable_tabs_to_links(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_tabs_to_links(settings));
+
+ g_assert(!webkit_settings_get_enable_dns_prefetching(settings));
+ webkit_settings_set_enable_dns_prefetching(settings, TRUE);
+ g_assert(webkit_settings_get_enable_dns_prefetching(settings));
+
+ // Caret browsing is disabled by default.
+ g_assert(!webkit_settings_get_enable_caret_browsing(settings));
+ webkit_settings_set_enable_caret_browsing(settings, TRUE);
+ g_assert(webkit_settings_get_enable_caret_browsing(settings));
+
+ // Fullscreen JavaScript API is enabled by default.
+ g_assert(webkit_settings_get_enable_fullscreen(settings));
+ webkit_settings_set_enable_fullscreen(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_fullscreen(settings));
+
+ // Print backgrounds is enabled by default
+ g_assert(webkit_settings_get_print_backgrounds(settings));
+ webkit_settings_set_print_backgrounds(settings, FALSE);
+ g_assert(!webkit_settings_get_print_backgrounds(settings));
+
+ // WebAudio is disabled by default.
+ g_assert(!webkit_settings_get_enable_webaudio(settings));
+ webkit_settings_set_enable_webaudio(settings, TRUE);
+ g_assert(webkit_settings_get_enable_webaudio(settings));
+
+ // WebGL is disabled by default.
+ g_assert(!webkit_settings_get_enable_webgl(settings));
+ webkit_settings_set_enable_webgl(settings, TRUE);
+ g_assert(webkit_settings_get_enable_webgl(settings));
+
+ // Allow Modal Dialogs is disabled by default.
+ g_assert(!webkit_settings_get_allow_modal_dialogs(settings));
+ webkit_settings_set_allow_modal_dialogs(settings, TRUE);
+ g_assert(webkit_settings_get_allow_modal_dialogs(settings));
+
+ // Zoom text only is disabled by default.
+ g_assert(!webkit_settings_get_zoom_text_only(settings));
+ webkit_settings_set_zoom_text_only(settings, TRUE);
+ g_assert(webkit_settings_get_zoom_text_only(settings));
+
+ // By default, JavaScript cannot access the clipboard.
+ g_assert(!webkit_settings_get_javascript_can_access_clipboard(settings));
+ webkit_settings_set_javascript_can_access_clipboard(settings, TRUE);
+ g_assert(webkit_settings_get_javascript_can_access_clipboard(settings));
+
+ // By default, media playback doesn't require user gestures.
+ g_assert(!webkit_settings_get_media_playback_requires_user_gesture(settings));
+ webkit_settings_set_media_playback_requires_user_gesture(settings, TRUE);
+ g_assert(webkit_settings_get_media_playback_requires_user_gesture(settings));
+
+ // By default, inline media playback is allowed
+ g_assert(webkit_settings_get_media_playback_allows_inline(settings));
+ webkit_settings_set_media_playback_allows_inline(settings, FALSE);
+ g_assert(!webkit_settings_get_media_playback_allows_inline(settings));
+
+ // By default, debug indicators are disabled.
+ g_assert(!webkit_settings_get_draw_compositing_indicators(settings));
+ webkit_settings_set_draw_compositing_indicators(settings, TRUE);
+ g_assert(webkit_settings_get_draw_compositing_indicators(settings));
+
+ // By default, site specific quirks are enabled.
+ g_assert(webkit_settings_get_enable_site_specific_quirks(settings));
+ webkit_settings_set_enable_site_specific_quirks(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_site_specific_quirks(settings));
+
+ // By default, page cache is enabled.
+ g_assert(webkit_settings_get_enable_page_cache(settings));
+ webkit_settings_set_enable_page_cache(settings, FALSE);
+ g_assert(!webkit_settings_get_enable_page_cache(settings));
+
+ // By default, smooth scrolling is disabled.
+ g_assert(!webkit_settings_get_enable_smooth_scrolling(settings));
+ webkit_settings_set_enable_smooth_scrolling(settings, TRUE);
+ g_assert(webkit_settings_get_enable_smooth_scrolling(settings));
+
+ // By default, accelerated 2D canvas is disabled.
+ g_assert(!webkit_settings_get_enable_accelerated_2d_canvas(settings));
+ webkit_settings_set_enable_accelerated_2d_canvas(settings, TRUE);
+ g_assert(webkit_settings_get_enable_accelerated_2d_canvas(settings));
+
+ // By default, writing of console messages to stdout is disabled.
+ g_assert(!webkit_settings_get_enable_write_console_messages_to_stdout(settings));
+ webkit_settings_set_enable_write_console_messages_to_stdout(settings, TRUE);
+ g_assert(webkit_settings_get_enable_write_console_messages_to_stdout(settings));
+
+ // MediaStream is disabled by default.
+ g_assert(!webkit_settings_get_enable_media_stream(settings));
+ webkit_settings_set_enable_media_stream(settings, TRUE);
+ g_assert(webkit_settings_get_enable_media_stream(settings));
+
+ // By default, SpatialNavigation is disabled
+ g_assert(!webkit_settings_get_enable_spatial_navigation(settings));
+ webkit_settings_set_enable_spatial_navigation(settings, TRUE);
+ g_assert(webkit_settings_get_enable_spatial_navigation(settings));
+
+ // MediaSource is disabled by default
+ g_assert(!webkit_settings_get_enable_mediasource(settings));
+ webkit_settings_set_enable_mediasource(settings, TRUE);
+ g_assert(webkit_settings_get_enable_mediasource(settings));
+
+ // File access from file URLs is not allowed by default.
+ g_assert(!webkit_settings_get_allow_file_access_from_file_urls(settings));
+ webkit_settings_set_allow_file_access_from_file_urls(settings, TRUE);
+ g_assert(webkit_settings_get_allow_file_access_from_file_urls(settings));
+
+ g_object_unref(G_OBJECT(settings));
+}
+
+void testWebKitSettingsNewWithSettings(Test* test, gconstpointer)
+{
+ GRefPtr<WebKitSettings> settings = adoptGRef(webkit_settings_new_with_settings(
+ "enable-javascript", FALSE,
+ "auto-load-images", FALSE,
+ "load-icons-ignoring-image-load-setting", TRUE,
+ nullptr));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(settings.get()));
+ g_assert(!webkit_settings_get_enable_javascript(settings.get()));
+ g_assert(!webkit_settings_get_auto_load_images(settings.get()));
+ g_assert(webkit_settings_get_load_icons_ignoring_image_load_setting(settings.get()));
+}
+
+static CString convertWebViewMainResourceDataToCString(WebViewTest* test)
+{
+ size_t mainResourceDataSize = 0;
+ const char* mainResourceData = test->mainResourceData(mainResourceDataSize);
+ return CString(mainResourceData, mainResourceDataSize);
+}
+
+static void assertThatUserAgentIsSentInHeaders(WebViewTest* test, const CString& userAgent)
+{
+ test->loadURI(gServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+ ASSERT_CMP_CSTRING(convertWebViewMainResourceDataToCString(test), ==, userAgent);
+}
+
+static void testWebKitSettingsUserAgent(WebViewTest* test, gconstpointer)
+{
+ GRefPtr<WebKitSettings> settings = adoptGRef(webkit_settings_new());
+ CString defaultUserAgent = webkit_settings_get_user_agent(settings.get());
+ webkit_web_view_set_settings(test->m_webView, settings.get());
+
+ g_assert(g_strstr_len(defaultUserAgent.data(), -1, "AppleWebKit"));
+ g_assert(g_strstr_len(defaultUserAgent.data(), -1, "Safari"));
+
+ webkit_settings_set_user_agent(settings.get(), 0);
+ g_assert_cmpstr(defaultUserAgent.data(), ==, webkit_settings_get_user_agent(settings.get()));
+ assertThatUserAgentIsSentInHeaders(test, defaultUserAgent.data());
+
+ webkit_settings_set_user_agent(settings.get(), "");
+ g_assert_cmpstr(defaultUserAgent.data(), ==, webkit_settings_get_user_agent(settings.get()));
+
+ const char* funkyUserAgent = "Funky!";
+ webkit_settings_set_user_agent(settings.get(), funkyUserAgent);
+ g_assert_cmpstr(funkyUserAgent, ==, webkit_settings_get_user_agent(settings.get()));
+ assertThatUserAgentIsSentInHeaders(test, funkyUserAgent);
+
+ webkit_settings_set_user_agent_with_application_details(settings.get(), "WebKitGTK+", 0);
+ const char* userAgentWithNullVersion = webkit_settings_get_user_agent(settings.get());
+ g_assert_cmpstr(g_strstr_len(userAgentWithNullVersion, -1, defaultUserAgent.data()), ==, userAgentWithNullVersion);
+ g_assert(g_strstr_len(userAgentWithNullVersion, -1, "WebKitGTK+"));
+
+ webkit_settings_set_user_agent_with_application_details(settings.get(), "WebKitGTK+", "");
+ g_assert_cmpstr(webkit_settings_get_user_agent(settings.get()), ==, userAgentWithNullVersion);
+
+ webkit_settings_set_user_agent_with_application_details(settings.get(), "WebCatGTK+", "3.4.5");
+ const char* newUserAgent = webkit_settings_get_user_agent(settings.get());
+ g_assert(g_strstr_len(newUserAgent, -1, "3.4.5"));
+ g_assert(g_strstr_len(newUserAgent, -1, "WebCatGTK+"));
+
+ GUniquePtr<char> applicationUserAgent(g_strdup_printf("%s %s", defaultUserAgent.data(), "WebCatGTK+/3.4.5"));
+ g_assert_cmpstr(applicationUserAgent.get(), ==, webkit_settings_get_user_agent(settings.get()));
+}
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (g_str_equal(path, "/")) {
+ const char* userAgent = soup_message_headers_get_one(message->request_headers, "User-Agent");
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_COPY, userAgent, strlen(userAgent));
+ soup_message_body_complete(message->response_body);
+ } else
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+}
+
+void beforeAll()
+{
+ gServer = new WebKitTestServer();
+ gServer->run(serverCallback);
+
+ Test::add("WebKitSettings", "webkit-settings", testWebKitSettings);
+ Test::add("WebKitSettings", "new-with-settings", testWebKitSettingsNewWithSettings);
+ WebViewTest::add("WebKitSettings", "user-agent", testWebKitSettingsUserAgent);
+}
+
+void afterAll()
+{
+ delete gServer;
+}
+
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitUserContentManager.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitUserContentManager.cpp
new file mode 100644
index 000000000..e84cb5c20
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitUserContentManager.cpp
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2013-2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebKitTestServer.h"
+#include "WebViewTest.h"
+#include <cstdarg>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+
+class UserContentManagerTest : public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(UserContentManagerTest);
+
+ UserContentManagerTest()
+ : WebViewTest(webkit_user_content_manager_new())
+ {
+ // A reference is leaked when passing the result of webkit_user_content_manager_new()
+ // directly to webkit_web_view_new_with_user_content_manager() above. Adopting the
+ // reference here avoids the leak.
+ m_userContentManager = adoptGRef(webkit_web_view_get_user_content_manager(m_webView));
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_userContentManager.get()));
+ }
+
+ GRefPtr<WebKitUserContentManager> m_userContentManager;
+};
+
+static WebKitTestServer* kServer;
+
+// These are all here so that they can be changed easily, if necessary.
+static const char* kStyleSheetHTML = "<html><div id=\"styledElement\">Sweet stylez!</div></html>";
+static const char* kInjectedStyleSheet = "#styledElement { font-weight: bold; }";
+static const char* kStyleSheetTestScript = "getComputedStyle(document.getElementById('styledElement'))['font-weight']";
+static const char* kStyleSheetTestScriptResult = "bold";
+static const char* kInjectedScript = "document.write('<div id=\"item\">Generated by a script</div>')";
+static const char* kScriptTestScript = "document.getElementById('item').innerText";
+static const char* kScriptTestScriptResult = "Generated by a script";
+
+static void testWebViewNewWithUserContentManager(Test* test, gconstpointer)
+{
+ GRefPtr<WebKitUserContentManager> userContentManager1 = adoptGRef(webkit_user_content_manager_new());
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(userContentManager1.get()));
+ GRefPtr<WebKitWebView> webView1 = WEBKIT_WEB_VIEW(webkit_web_view_new_with_user_content_manager(userContentManager1.get()));
+ g_assert(webkit_web_view_get_user_content_manager(webView1.get()) == userContentManager1.get());
+
+ GRefPtr<WebKitWebView> webView2 = WEBKIT_WEB_VIEW(webkit_web_view_new());
+ g_assert(webkit_web_view_get_user_content_manager(webView2.get()) != userContentManager1.get());
+}
+
+static bool isStyleSheetInjectedForURLAtPath(WebViewTest* test, const char* path)
+{
+ test->loadURI(kServer->getURIForPath(path).data());
+ test->waitUntilLoadFinished();
+
+ GUniqueOutPtr<GError> error;
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(kStyleSheetTestScript, &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+
+ GUniquePtr<char> resultString(WebViewTest::javascriptResultToCString(javascriptResult));
+ return !g_strcmp0(resultString.get(), kStyleSheetTestScriptResult);
+}
+
+static bool isScriptInjectedForURLAtPath(WebViewTest* test, const char* path)
+{
+ test->loadURI(kServer->getURIForPath(path).data());
+ test->waitUntilLoadFinished();
+
+ GUniqueOutPtr<GError> error;
+ if (WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(kScriptTestScript, &error.outPtr())) {
+ g_assert(!error.get());
+
+ GUniquePtr<char> resultString(WebViewTest::javascriptResultToCString(javascriptResult));
+ return !g_strcmp0(resultString.get(), kScriptTestScriptResult);
+ }
+ return false;
+}
+
+static void fillURLListFromPaths(char** list, const char* path, ...)
+{
+ va_list argumentList;
+ va_start(argumentList, path);
+
+ int i = 0;
+ while (path) {
+ // FIXME: We must use a wildcard for the host here until http://wkbug.com/112476 is fixed.
+ // Until that time patterns with port numbers in them will not properly match URLs with port numbers.
+ list[i++] = g_strdup_printf("http://*/%s*", path);
+ path = va_arg(argumentList, const char*);
+ }
+}
+
+static void removeOldInjectedContentAndResetLists(WebKitUserContentManager* userContentManager, char** whitelist, char** blacklist)
+{
+ webkit_user_content_manager_remove_all_style_sheets(userContentManager);
+ webkit_user_content_manager_remove_all_scripts(userContentManager);
+
+ while (*whitelist) {
+ g_free(*whitelist);
+ *whitelist = 0;
+ whitelist++;
+ }
+
+ while (*blacklist) {
+ g_free(*blacklist);
+ *blacklist = 0;
+ blacklist++;
+ }
+}
+
+static void testUserContentManagerInjectedStyleSheet(UserContentManagerTest* test, gconstpointer)
+{
+ char* whitelist[3] = { 0, 0, 0 };
+ char* blacklist[3] = { 0, 0, 0 };
+
+ removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist);
+
+ // Without a whitelist or a blacklist all URLs should have the injected style sheet.
+ static const char* randomPath = "somerandompath";
+ g_assert(!isStyleSheetInjectedForURLAtPath(test, randomPath));
+ WebKitUserStyleSheet* styleSheet = webkit_user_style_sheet_new(kInjectedStyleSheet, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_STYLE_LEVEL_USER, nullptr, nullptr);
+ webkit_user_content_manager_add_style_sheet(test->m_userContentManager.get(), styleSheet);
+ webkit_user_style_sheet_unref(styleSheet);
+ g_assert(isStyleSheetInjectedForURLAtPath(test, randomPath));
+
+ removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist);
+
+ fillURLListFromPaths(blacklist, randomPath, 0);
+ styleSheet = webkit_user_style_sheet_new(kInjectedStyleSheet, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_STYLE_LEVEL_USER, nullptr, blacklist);
+ webkit_user_content_manager_add_style_sheet(test->m_userContentManager.get(), styleSheet);
+ webkit_user_style_sheet_unref(styleSheet);
+ g_assert(!isStyleSheetInjectedForURLAtPath(test, randomPath));
+ g_assert(isStyleSheetInjectedForURLAtPath(test, "someotherrandompath"));
+
+ removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist);
+
+ static const char* inTheWhiteList = "inthewhitelist";
+ static const char* notInWhitelist = "notinthewhitelist";
+ static const char* inTheWhiteListAndBlackList = "inthewhitelistandblacklist";
+
+ fillURLListFromPaths(whitelist, inTheWhiteList, inTheWhiteListAndBlackList, 0);
+ fillURLListFromPaths(blacklist, inTheWhiteListAndBlackList, 0);
+ styleSheet = webkit_user_style_sheet_new(kInjectedStyleSheet, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_STYLE_LEVEL_USER, whitelist, blacklist);
+ webkit_user_content_manager_add_style_sheet(test->m_userContentManager.get(), styleSheet);
+ webkit_user_style_sheet_unref(styleSheet);
+ g_assert(isStyleSheetInjectedForURLAtPath(test, inTheWhiteList));
+ g_assert(!isStyleSheetInjectedForURLAtPath(test, inTheWhiteListAndBlackList));
+ g_assert(!isStyleSheetInjectedForURLAtPath(test, notInWhitelist));
+
+ // It's important to clean up the environment before other tests.
+ removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist);
+}
+
+static void testUserContentManagerInjectedScript(UserContentManagerTest* test, gconstpointer)
+{
+ char* whitelist[3] = { 0, 0, 0 };
+ char* blacklist[3] = { 0, 0, 0 };
+
+ removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist);
+
+ // Without a whitelist or a blacklist all URLs should have the injected script.
+ static const char* randomPath = "somerandompath";
+ g_assert(!isScriptInjectedForURLAtPath(test, randomPath));
+ WebKitUserScript* script = webkit_user_script_new(kInjectedScript, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, nullptr, nullptr);
+ webkit_user_content_manager_add_script(test->m_userContentManager.get(), script);
+ webkit_user_script_unref(script);
+ g_assert(isScriptInjectedForURLAtPath(test, randomPath));
+
+ removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist);
+
+ fillURLListFromPaths(blacklist, randomPath, 0);
+ script = webkit_user_script_new(kInjectedScript, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, nullptr, blacklist);
+ webkit_user_content_manager_add_script(test->m_userContentManager.get(), script);
+ webkit_user_script_unref(script);
+ g_assert(!isScriptInjectedForURLAtPath(test, randomPath));
+ g_assert(isScriptInjectedForURLAtPath(test, "someotherrandompath"));
+
+ removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist);
+
+ static const char* inTheWhiteList = "inthewhitelist";
+ static const char* notInWhitelist = "notinthewhitelist";
+ static const char* inTheWhiteListAndBlackList = "inthewhitelistandblacklist";
+
+ fillURLListFromPaths(whitelist, inTheWhiteList, inTheWhiteListAndBlackList, 0);
+ fillURLListFromPaths(blacklist, inTheWhiteListAndBlackList, 0);
+ script = webkit_user_script_new(kInjectedScript, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, whitelist, blacklist);
+ webkit_user_content_manager_add_script(test->m_userContentManager.get(), script);
+ webkit_user_script_unref(script);
+ g_assert(isScriptInjectedForURLAtPath(test, inTheWhiteList));
+ g_assert(!isScriptInjectedForURLAtPath(test, inTheWhiteListAndBlackList));
+ g_assert(!isScriptInjectedForURLAtPath(test, notInWhitelist));
+
+ // It's important to clean up the environment before other tests.
+ removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist);
+}
+
+class UserScriptMessageTest : public UserContentManagerTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(UserScriptMessageTest);
+
+ UserScriptMessageTest()
+ : UserContentManagerTest()
+ , m_userScriptMessage(nullptr)
+ {
+ }
+
+ ~UserScriptMessageTest()
+ {
+ if (m_userScriptMessage)
+ webkit_javascript_result_unref(m_userScriptMessage);
+ }
+
+ bool registerHandler(const char* handlerName)
+ {
+ return webkit_user_content_manager_register_script_message_handler(m_userContentManager.get(), handlerName);
+ }
+
+ void unregisterHandler(const char* handlerName)
+ {
+ webkit_user_content_manager_unregister_script_message_handler(m_userContentManager.get(), handlerName);
+ }
+
+ static void scriptMessageReceived(WebKitUserContentManager* userContentManager, WebKitJavascriptResult* jsResult, UserScriptMessageTest* test)
+ {
+ g_signal_handlers_disconnect_by_func(userContentManager, reinterpret_cast<gpointer>(scriptMessageReceived), test);
+ g_main_loop_quit(test->m_mainLoop);
+
+ g_assert(!test->m_userScriptMessage);
+ test->m_userScriptMessage = webkit_javascript_result_ref(jsResult);
+ }
+
+ WebKitJavascriptResult* waitUntilMessageReceived(const char* handlerName)
+ {
+ if (m_userScriptMessage) {
+ webkit_javascript_result_unref(m_userScriptMessage);
+ m_userScriptMessage = nullptr;
+ }
+
+ GUniquePtr<char> signalName(g_strdup_printf("script-message-received::%s", handlerName));
+ g_signal_connect(m_userContentManager.get(), signalName.get(), G_CALLBACK(scriptMessageReceived), this);
+
+ g_main_loop_run(m_mainLoop);
+ g_assert(m_userScriptMessage);
+ return m_userScriptMessage;
+ }
+
+ WebKitJavascriptResult* postMessageAndWaitUntilReceived(const char* handlerName, const char* javascriptValueAsText)
+ {
+ GUniquePtr<char> javascriptSnippet(g_strdup_printf("window.webkit.messageHandlers.%s.postMessage(%s);", handlerName, javascriptValueAsText));
+ webkit_web_view_run_javascript(m_webView, javascriptSnippet.get(), nullptr, nullptr, nullptr);
+ return waitUntilMessageReceived(handlerName);
+ }
+
+private:
+ WebKitJavascriptResult* m_userScriptMessage;
+};
+
+static void testUserContentManagerScriptMessageReceived(UserScriptMessageTest* test, gconstpointer)
+{
+ g_assert(test->registerHandler("msg"));
+
+ // Trying to register the same handler a second time must fail.
+ g_assert(!test->registerHandler("msg"));
+
+ test->loadHtml("<html></html>", nullptr);
+ test->waitUntilLoadFinished();
+
+ // Check that the "window.webkit.messageHandlers" namespace exists.
+ GUniqueOutPtr<GError> error;
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers ? 'y' : 'n';", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "y");
+
+ // Check that the "document.webkit.messageHandlers.msg" namespace exists.
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers.msg ? 'y' : 'n';", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "y");
+
+ valueString.reset(WebViewTest::javascriptResultToCString(test->postMessageAndWaitUntilReceived("msg", "'user message'")));
+ g_assert_cmpstr(valueString.get(), ==, "user message");
+
+ // Messages should arrive despite of other handlers being registered.
+ g_assert(test->registerHandler("anotherHandler"));
+
+ // Check that the "document.webkit.messageHandlers.msg" namespace still exists.
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers.msg ? 'y' : 'n';", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "y");
+
+ // Check that the "document.webkit.messageHandlers.anotherHandler" namespace exists.
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers.anotherHandler ? 'y' : 'n';", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "y");
+
+ valueString.reset(WebViewTest::javascriptResultToCString(test->postMessageAndWaitUntilReceived("msg", "'handler: msg'")));
+ g_assert_cmpstr(valueString.get(), ==, "handler: msg");
+
+ valueString.reset(WebViewTest::javascriptResultToCString(test->postMessageAndWaitUntilReceived("anotherHandler", "'handler: anotherHandler'")));
+ g_assert_cmpstr(valueString.get(), ==, "handler: anotherHandler");
+
+ // Unregistering a handler and re-registering again under the same name should work.
+ test->unregisterHandler("msg");
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers.msg.postMessage('42');", &error.outPtr());
+ g_assert(!javascriptResult);
+ g_assert(error.get());
+
+ // Re-registering a handler that has been unregistered must work
+ g_assert(test->registerHandler("msg"));
+ valueString.reset(WebViewTest::javascriptResultToCString(test->postMessageAndWaitUntilReceived("msg", "'handler: msg'")));
+ g_assert_cmpstr(valueString.get(), ==, "handler: msg");
+
+ test->unregisterHandler("anotherHandler");
+}
+
+static void testUserContentManagerScriptMessageFromDOMBindings(UserScriptMessageTest* test, gconstpointer)
+{
+ g_assert(test->registerHandler("dom"));
+
+ test->loadHtml("<html>1</html>", nullptr);
+ WebKitJavascriptResult* javascriptResult = test->waitUntilMessageReceived("dom");
+ g_assert(javascriptResult);
+ GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "DocumentLoaded");
+
+ test->unregisterHandler("dom");
+
+ g_assert(test->registerHandler("dom-convenience"));
+
+ test->loadHtml("<html>2</html>", nullptr);
+ javascriptResult = test->waitUntilMessageReceived("dom-convenience");
+ g_assert(javascriptResult);
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "DocumentLoaded");
+
+ test->unregisterHandler("dom-convenience");
+}
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kStyleSheetHTML, strlen(kStyleSheetHTML));
+ soup_message_body_complete(message->response_body);
+}
+
+void beforeAll()
+{
+ kServer = new WebKitTestServer();
+ kServer->run(serverCallback);
+
+ Test::add("WebKitWebView", "new-with-user-content-manager", testWebViewNewWithUserContentManager);
+ UserContentManagerTest::add("WebKitUserContentManager", "injected-style-sheet", testUserContentManagerInjectedStyleSheet);
+ UserContentManagerTest::add("WebKitUserContentManager", "injected-script", testUserContentManagerInjectedScript);
+ UserScriptMessageTest::add("WebKitUserContentManager", "script-message-received", testUserContentManagerScriptMessageReceived);
+ UserScriptMessageTest::add("WebKitUserContentManager", "script-message-from-dom-bindings", testUserContentManagerScriptMessageFromDOMBindings);
+}
+
+void afterAll()
+{
+ delete kServer;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitVersion.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitVersion.cpp
new file mode 100644
index 000000000..e747ff962
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitVersion.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "TestMain.h"
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+
+static void testWebKitVersion(Test*, gconstpointer)
+{
+ g_assert_cmpuint(webkit_get_major_version(), ==, WEBKIT_MAJOR_VERSION);
+ g_assert_cmpuint(webkit_get_minor_version(), ==, WEBKIT_MINOR_VERSION);
+ g_assert_cmpuint(webkit_get_micro_version(), ==, WEBKIT_MICRO_VERSION);
+}
+
+static void testWebKitCheckVersion(Test*, gconstpointer)
+{
+ g_assert(WEBKIT_CHECK_VERSION(WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION));
+ g_assert(!WEBKIT_CHECK_VERSION(WEBKIT_MAJOR_VERSION + 1, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION));
+ g_assert(!WEBKIT_CHECK_VERSION(WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION + 1, WEBKIT_MICRO_VERSION));
+ g_assert(!WEBKIT_CHECK_VERSION(WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION + 1));
+}
+
+void beforeAll()
+{
+ Test::add("WebKitVersion", "version", testWebKitVersion);
+ Test::add("WebKitVersion", "check-version", testWebKitCheckVersion);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp
new file mode 100644
index 000000000..96a34b150
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "LoadTrackingTest.h"
+#include "WebKitTestServer.h"
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+#include <wtf/HashMap.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/StringHash.h>
+
+static WebKitTestServer* kServer;
+
+static void testWebContextDefault(Test* test, gconstpointer)
+{
+ // Check there's a single instance of the default web context.
+ g_assert(webkit_web_context_get_default() == webkit_web_context_get_default());
+ g_assert(webkit_web_context_get_default() != test->m_webContext.get());
+}
+
+static void testWebContextConfiguration(WebViewTest* test, gconstpointer)
+{
+ WebKitWebsiteDataManager* manager = webkit_web_context_get_website_data_manager(test->m_webContext.get());
+ g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(manager));
+
+ // Base directories are not used by TestMain.
+ g_assert(!webkit_website_data_manager_get_base_data_directory(manager));
+ g_assert(!webkit_website_data_manager_get_base_cache_directory(manager));
+
+ GUniquePtr<char> localStorageDirectory(g_build_filename(Test::dataDirectory(), "local-storage", nullptr));
+ g_assert_cmpstr(localStorageDirectory.get(), ==, webkit_website_data_manager_get_local_storage_directory(manager));
+ g_assert(g_file_test(localStorageDirectory.get(), G_FILE_TEST_IS_DIR));
+
+ test->loadURI(kServer->getURIForPath("/empty").data());
+ test->waitUntilLoadFinished();
+ test->runJavaScriptAndWaitUntilFinished("window.indexedDB.open('TestDatabase');", nullptr);
+ GUniquePtr<char> indexedDBDirectory(g_build_filename(Test::dataDirectory(), "indexeddb", nullptr));
+ g_assert_cmpstr(indexedDBDirectory.get(), ==, webkit_website_data_manager_get_indexeddb_directory(manager));
+ g_assert(g_file_test(indexedDBDirectory.get(), G_FILE_TEST_IS_DIR));
+
+ test->loadURI(kServer->getURIForPath("/appcache").data());
+ test->waitUntilLoadFinished();
+ GUniquePtr<char> applicationCacheDirectory(g_build_filename(Test::dataDirectory(), "appcache", nullptr));
+ g_assert_cmpstr(applicationCacheDirectory.get(), ==, webkit_website_data_manager_get_offline_application_cache_directory(manager));
+ GUniquePtr<char> applicationCacheDatabase(g_build_filename(applicationCacheDirectory.get(), "ApplicationCache.db", nullptr));
+ unsigned triesCount = 4;
+ while (!g_file_test(applicationCacheDatabase.get(), G_FILE_TEST_IS_REGULAR) && --triesCount)
+ test->wait(0.25);
+ g_assert(triesCount);
+
+
+ GUniquePtr<char> webSQLDirectory(g_build_filename(Test::dataDirectory(), "websql", nullptr));
+ g_assert_cmpstr(webSQLDirectory.get(), ==, webkit_website_data_manager_get_websql_directory(manager));
+ test->runJavaScriptAndWaitUntilFinished("db = openDatabase(\"TestDatabase\", \"1.0\", \"TestDatabase\", 1);", nullptr);
+ g_assert(g_file_test(webSQLDirectory.get(), G_FILE_TEST_IS_DIR));
+
+ GUniquePtr<char> diskCacheDirectory(g_build_filename(Test::dataDirectory(), "disk-cache", nullptr));
+ g_assert_cmpstr(diskCacheDirectory.get(), ==, webkit_website_data_manager_get_disk_cache_directory(manager));
+ g_assert(g_file_test(diskCacheDirectory.get(), G_FILE_TEST_IS_DIR));
+
+ // The default context should have a different manager with different configuration.
+ WebKitWebsiteDataManager* defaultManager = webkit_web_context_get_website_data_manager(webkit_web_context_get_default());
+ g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(defaultManager));
+ g_assert(manager != defaultManager);
+ g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(manager), !=, webkit_website_data_manager_get_local_storage_directory(defaultManager));
+ g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(manager), !=, webkit_website_data_manager_get_indexeddb_directory(defaultManager));
+ g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(manager), !=, webkit_website_data_manager_get_disk_cache_directory(defaultManager));
+ g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(manager), !=, webkit_website_data_manager_get_offline_application_cache_directory(defaultManager));
+ g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(manager), !=, webkit_website_data_manager_get_websql_directory(defaultManager));
+
+ // Using Test::dataDirectory() we get the default configuration but for a differrent prefix.
+ GRefPtr<WebKitWebsiteDataManager> baseDataManager = adoptGRef(webkit_website_data_manager_new("base-data-directory", Test::dataDirectory(), "base-cache-directory", Test::dataDirectory(), nullptr));
+ g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(baseDataManager.get()));
+
+ localStorageDirectory.reset(g_build_filename(Test::dataDirectory(), "localstorage", nullptr));
+ g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(baseDataManager.get()), ==, localStorageDirectory.get());
+
+ indexedDBDirectory.reset(g_build_filename(Test::dataDirectory(), "databases", "indexeddb", nullptr));
+ g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(baseDataManager.get()), ==, indexedDBDirectory.get());
+
+ applicationCacheDirectory.reset(g_build_filename(Test::dataDirectory(), "applications", nullptr));
+ g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(baseDataManager.get()), ==, applicationCacheDirectory.get());
+
+ webSQLDirectory.reset(g_build_filename(Test::dataDirectory(), "databases", nullptr));
+ g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(baseDataManager.get()), ==, webSQLDirectory.get());
+
+ g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(baseDataManager.get()), ==, Test::dataDirectory());
+
+ // Any specific configuration provided takes precedence over base dirs.
+ indexedDBDirectory.reset(g_build_filename(Test::dataDirectory(), "mycustomindexeddb", nullptr));
+ applicationCacheDirectory.reset(g_build_filename(Test::dataDirectory(), "mycustomappcache", nullptr));
+ baseDataManager = adoptGRef(webkit_website_data_manager_new("base-data-directory", Test::dataDirectory(), "base-cache-directory", Test::dataDirectory(),
+ "indexeddb-directory", indexedDBDirectory.get(), "offline-application-cache-directory", applicationCacheDirectory.get(), nullptr));
+ g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(baseDataManager.get()), ==, indexedDBDirectory.get());
+ g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(baseDataManager.get()), ==, applicationCacheDirectory.get());
+ // The resutl should be the same as previous manager.
+ g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(baseDataManager.get()), ==, localStorageDirectory.get());
+ g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(baseDataManager.get()), ==, webSQLDirectory.get());
+ g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(baseDataManager.get()), ==, Test::dataDirectory());
+}
+
+class PluginsTest: public Test {
+public:
+ MAKE_GLIB_TEST_FIXTURE(PluginsTest);
+
+ PluginsTest()
+ : m_mainLoop(g_main_loop_new(nullptr, TRUE))
+ , m_plugins(nullptr)
+ {
+ webkit_web_context_set_additional_plugins_directory(m_webContext.get(), WEBKIT_TEST_PLUGIN_DIR);
+ }
+
+ ~PluginsTest()
+ {
+ g_main_loop_unref(m_mainLoop);
+ g_list_free_full(m_plugins, g_object_unref);
+ }
+
+ static void getPluginsAsyncReadyCallback(GObject*, GAsyncResult* result, PluginsTest* test)
+ {
+ test->m_plugins = webkit_web_context_get_plugins_finish(test->m_webContext.get(), result, nullptr);
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ GList* getPlugins()
+ {
+ g_list_free_full(m_plugins, g_object_unref);
+ webkit_web_context_get_plugins(m_webContext.get(), nullptr, reinterpret_cast<GAsyncReadyCallback>(getPluginsAsyncReadyCallback), this);
+ g_main_loop_run(m_mainLoop);
+ return m_plugins;
+ }
+
+ GMainLoop* m_mainLoop;
+ GList* m_plugins;
+};
+
+static void testWebContextGetPlugins(PluginsTest* test, gconstpointer)
+{
+ GList* plugins = test->getPlugins();
+ g_assert(plugins);
+
+ GRefPtr<WebKitPlugin> testPlugin;
+ for (GList* item = plugins; item; item = g_list_next(item)) {
+ WebKitPlugin* plugin = WEBKIT_PLUGIN(item->data);
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(plugin));
+ if (!g_strcmp0(webkit_plugin_get_name(plugin), "WebKit Test PlugIn")) {
+ testPlugin = plugin;
+ break;
+ }
+ }
+ g_assert(WEBKIT_IS_PLUGIN(testPlugin.get()));
+
+ GUniquePtr<char> pluginPath(g_build_filename(WEBKIT_TEST_PLUGIN_DIR, "libTestNetscapePlugin.so", nullptr));
+ g_assert_cmpstr(webkit_plugin_get_path(testPlugin.get()), ==, pluginPath.get());
+ g_assert_cmpstr(webkit_plugin_get_description(testPlugin.get()), ==, "Simple Netscape® plug-in that handles test content for WebKit");
+ GList* mimeInfoList = webkit_plugin_get_mime_info_list(testPlugin.get());
+ g_assert(mimeInfoList);
+ g_assert_cmpuint(g_list_length(mimeInfoList), ==, 2);
+
+ WebKitMimeInfo* mimeInfo = static_cast<WebKitMimeInfo*>(mimeInfoList->data);
+ g_assert_cmpstr(webkit_mime_info_get_mime_type(mimeInfo), ==, "image/png");
+ g_assert_cmpstr(webkit_mime_info_get_description(mimeInfo), ==, "png image");
+ const gchar* const* extensions = webkit_mime_info_get_extensions(mimeInfo);
+ g_assert(extensions);
+ g_assert_cmpstr(extensions[0], ==, "png");
+
+ mimeInfoList = g_list_next(mimeInfoList);
+ mimeInfo = static_cast<WebKitMimeInfo*>(mimeInfoList->data);
+ g_assert_cmpstr(webkit_mime_info_get_mime_type(mimeInfo), ==, "application/x-webkit-test-netscape");
+ g_assert_cmpstr(webkit_mime_info_get_description(mimeInfo), ==, "test netscape content");
+ extensions = webkit_mime_info_get_extensions(mimeInfo);
+ g_assert(extensions);
+ g_assert_cmpstr(extensions[0], ==, "testnetscape");
+}
+
+static const char* kBarHTML = "<html><body>Bar</body></html>";
+static const char* kEchoHTMLFormat = "<html><body>%s</body></html>";
+static const char* errorDomain = "test";
+static const int errorCode = 10;
+static const char* errorMessage = "Error message.";
+
+class URISchemeTest: public LoadTrackingTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(URISchemeTest);
+
+ struct URISchemeHandler {
+ URISchemeHandler()
+ : replyLength(0)
+ {
+ }
+
+ URISchemeHandler(const char* reply, int replyLength, const char* mimeType)
+ : reply(reply)
+ , replyLength(replyLength)
+ , mimeType(mimeType)
+ {
+ }
+
+ CString reply;
+ int replyLength;
+ CString mimeType;
+ };
+
+ static void uriSchemeRequestCallback(WebKitURISchemeRequest* request, gpointer userData)
+ {
+ URISchemeTest* test = static_cast<URISchemeTest*>(userData);
+ test->m_uriSchemeRequest = request;
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
+
+ g_assert(webkit_uri_scheme_request_get_web_view(request) == test->m_webView);
+
+ GRefPtr<GInputStream> inputStream = adoptGRef(g_memory_input_stream_new());
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(inputStream.get()));
+
+ const char* scheme = webkit_uri_scheme_request_get_scheme(request);
+ g_assert(scheme);
+ g_assert(test->m_handlersMap.contains(String::fromUTF8(scheme)));
+
+ if (!g_strcmp0(scheme, "error")) {
+ GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, errorMessage));
+ webkit_uri_scheme_request_finish_error(request, error.get());
+ return;
+ }
+
+ const URISchemeHandler& handler = test->m_handlersMap.get(String::fromUTF8(scheme));
+
+ if (!g_strcmp0(scheme, "echo")) {
+ char* replyHTML = g_strdup_printf(handler.reply.data(), webkit_uri_scheme_request_get_path(request));
+ g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(inputStream.get()), replyHTML, strlen(replyHTML), g_free);
+ } else if (!g_strcmp0(scheme, "closed"))
+ g_input_stream_close(inputStream.get(), 0, 0);
+ else if (!handler.reply.isNull())
+ g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(inputStream.get()), handler.reply.data(), handler.reply.length(), 0);
+
+ webkit_uri_scheme_request_finish(request, inputStream.get(), handler.replyLength, handler.mimeType.data());
+ }
+
+ void registerURISchemeHandler(const char* scheme, const char* reply, int replyLength, const char* mimeType)
+ {
+ m_handlersMap.set(String::fromUTF8(scheme), URISchemeHandler(reply, replyLength, mimeType));
+ webkit_web_context_register_uri_scheme(m_webContext.get(), scheme, uriSchemeRequestCallback, this, 0);
+ }
+
+ GRefPtr<WebKitURISchemeRequest> m_uriSchemeRequest;
+ HashMap<String, URISchemeHandler> m_handlersMap;
+};
+
+static void testWebContextURIScheme(URISchemeTest* test, gconstpointer)
+{
+ test->registerURISchemeHandler("foo", kBarHTML, strlen(kBarHTML), "text/html");
+ test->loadURI("foo:blank");
+ test->waitUntilLoadFinished();
+ size_t mainResourceDataSize = 0;
+ const char* mainResourceData = test->mainResourceData(mainResourceDataSize);
+ g_assert_cmpint(mainResourceDataSize, ==, strlen(kBarHTML));
+ g_assert(!strncmp(mainResourceData, kBarHTML, mainResourceDataSize));
+
+ test->registerURISchemeHandler("echo", kEchoHTMLFormat, -1, "text/html");
+ test->loadURI("echo:hello-world");
+ test->waitUntilLoadFinished();
+ GUniquePtr<char> echoHTML(g_strdup_printf(kEchoHTMLFormat, webkit_uri_scheme_request_get_path(test->m_uriSchemeRequest.get())));
+ mainResourceDataSize = 0;
+ mainResourceData = test->mainResourceData(mainResourceDataSize);
+ g_assert_cmpint(mainResourceDataSize, ==, strlen(echoHTML.get()));
+ g_assert(!strncmp(mainResourceData, echoHTML.get(), mainResourceDataSize));
+
+ test->loadURI("echo:with#fragment");
+ test->waitUntilLoadFinished();
+ g_assert_cmpstr(webkit_uri_scheme_request_get_path(test->m_uriSchemeRequest.get()), ==, "with");
+ g_assert_cmpstr(webkit_uri_scheme_request_get_uri(test->m_uriSchemeRequest.get()), ==, "echo:with#fragment");
+ echoHTML.reset(g_strdup_printf(kEchoHTMLFormat, webkit_uri_scheme_request_get_path(test->m_uriSchemeRequest.get())));
+ mainResourceDataSize = 0;
+ mainResourceData = test->mainResourceData(mainResourceDataSize);
+ g_assert_cmpint(mainResourceDataSize, ==, strlen(echoHTML.get()));
+ g_assert(!strncmp(mainResourceData, echoHTML.get(), mainResourceDataSize));
+
+ test->registerURISchemeHandler("nomime", kBarHTML, -1, 0);
+ test->m_loadEvents.clear();
+ test->loadURI("nomime:foo-bar");
+ test->waitUntilLoadFinished();
+ g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
+
+ test->registerURISchemeHandler("empty", 0, 0, "text/html");
+ test->m_loadEvents.clear();
+ test->loadURI("empty:nothing");
+ test->waitUntilLoadFinished();
+ g_assert(!test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
+ g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadFailed));
+
+ test->registerURISchemeHandler("error", 0, 0, 0);
+ test->m_loadEvents.clear();
+ test->loadURI("error:error");
+ test->waitUntilLoadFinished();
+ g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
+ g_assert(test->m_loadFailed);
+ g_assert_error(test->m_error.get(), g_quark_from_string(errorDomain), errorCode);
+ g_assert_cmpstr(test->m_error->message, ==, errorMessage);
+
+ test->registerURISchemeHandler("closed", 0, 0, 0);
+ test->m_loadEvents.clear();
+ test->loadURI("closed:input-stream");
+ test->waitUntilLoadFinished();
+ g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
+ g_assert(test->m_loadFailed);
+ g_assert_error(test->m_error.get(), G_IO_ERROR, G_IO_ERROR_CLOSED);
+}
+
+static void testWebContextSpellChecker(Test* test, gconstpointer)
+{
+ WebKitWebContext* webContext = test->m_webContext.get();
+
+ // Check what happens if no spell checking language has been set.
+ const gchar* const* currentLanguage = webkit_web_context_get_spell_checking_languages(webContext);
+ g_assert(!currentLanguage);
+
+ // Set the language to a specific one.
+ GRefPtr<GPtrArray> languages = adoptGRef(g_ptr_array_new());
+ g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("en_US")));
+ g_ptr_array_add(languages.get(), 0);
+ webkit_web_context_set_spell_checking_languages(webContext, reinterpret_cast<const char* const*>(languages->pdata));
+ currentLanguage = webkit_web_context_get_spell_checking_languages(webContext);
+ g_assert_cmpuint(g_strv_length(const_cast<char**>(currentLanguage)), ==, 1);
+ g_assert_cmpstr(currentLanguage[0], ==, "en_US");
+
+ // Set the language string to list of valid languages.
+ g_ptr_array_remove_index_fast(languages.get(), languages->len - 1);
+ g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("en_GB")));
+ g_ptr_array_add(languages.get(), 0);
+ webkit_web_context_set_spell_checking_languages(webContext, reinterpret_cast<const char* const*>(languages->pdata));
+ currentLanguage = webkit_web_context_get_spell_checking_languages(webContext);
+ g_assert_cmpuint(g_strv_length(const_cast<char**>(currentLanguage)), ==, 2);
+ g_assert_cmpstr(currentLanguage[0], ==, "en_US");
+ g_assert_cmpstr(currentLanguage[1], ==, "en_GB");
+
+ // Try passing a wrong language along with good ones.
+ g_ptr_array_remove_index_fast(languages.get(), languages->len - 1);
+ g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("bd_WR")));
+ g_ptr_array_add(languages.get(), 0);
+ webkit_web_context_set_spell_checking_languages(webContext, reinterpret_cast<const char* const*>(languages->pdata));
+ currentLanguage = webkit_web_context_get_spell_checking_languages(webContext);
+ g_assert_cmpuint(g_strv_length(const_cast<char**>(currentLanguage)), ==, 2);
+ g_assert_cmpstr(currentLanguage[0], ==, "en_US");
+ g_assert_cmpstr(currentLanguage[1], ==, "en_GB");
+
+ // Try passing a list with only wrong languages.
+ languages = adoptGRef(g_ptr_array_new());
+ g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("bd_WR")));
+ g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("wr_BD")));
+ g_ptr_array_add(languages.get(), 0);
+ webkit_web_context_set_spell_checking_languages(webContext, reinterpret_cast<const char* const*>(languages->pdata));
+ currentLanguage = webkit_web_context_get_spell_checking_languages(webContext);
+ g_assert(!currentLanguage);
+
+ // Check disabling and re-enabling spell checking.
+ webkit_web_context_set_spell_checking_enabled(webContext, FALSE);
+ g_assert(!webkit_web_context_get_spell_checking_enabled(webContext));
+ webkit_web_context_set_spell_checking_enabled(webContext, TRUE);
+ g_assert(webkit_web_context_get_spell_checking_enabled(webContext));
+}
+
+static void testWebContextLanguages(WebViewTest* test, gconstpointer)
+{
+ static const char* expectedDefaultLanguage = "en";
+ test->loadURI(kServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+ size_t mainResourceDataSize = 0;
+ const char* mainResourceData = test->mainResourceData(mainResourceDataSize);
+ g_assert_cmpuint(mainResourceDataSize, ==, strlen(expectedDefaultLanguage));
+ g_assert(!strncmp(mainResourceData, expectedDefaultLanguage, mainResourceDataSize));
+
+ GRefPtr<GPtrArray> languages = adoptGRef(g_ptr_array_new());
+ g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("en")));
+ g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("ES_es")));
+ g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("dE")));
+ g_ptr_array_add(languages.get(), 0);
+ webkit_web_context_set_preferred_languages(test->m_webContext.get(), reinterpret_cast<const char* const*>(languages->pdata));
+
+ static const char* expectedLanguages = "en, es-es;q=0.90, de;q=0.80";
+ test->loadURI(kServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+ mainResourceDataSize = 0;
+ mainResourceData = test->mainResourceData(mainResourceDataSize);
+ g_assert_cmpuint(mainResourceDataSize, ==, strlen(expectedLanguages));
+ g_assert(!strncmp(mainResourceData, expectedLanguages, mainResourceDataSize));
+}
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (g_str_equal(path, "/")) {
+ const char* acceptLanguage = soup_message_headers_get_one(message->request_headers, "Accept-Language");
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_append(message->response_body, SOUP_MEMORY_COPY, acceptLanguage, strlen(acceptLanguage));
+ soup_message_body_complete(message->response_body);
+ } else if (g_str_equal(path, "/empty")) {
+ const char* emptyHTML = "<html><body></body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, emptyHTML, strlen(emptyHTML));
+ soup_message_body_complete(message->response_body);
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ } else if (g_str_equal(path, "/appcache")) {
+ const char* appcacheHTML = "<html manifest=appcache.manifest><body></body></html>";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, appcacheHTML, strlen(appcacheHTML));
+ soup_message_body_complete(message->response_body);
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ } else if (g_str_equal(path, "/appcache.manifest")) {
+ const char* appcacheManifest = "CACHE MANIFEST\nCACHE:\nappcache/foo.txt\n";
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, appcacheManifest, strlen(appcacheManifest));
+ soup_message_body_complete(message->response_body);
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ } else if (g_str_equal(path, "/appcache/foo.txt")) {
+ soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, "foo", 3);
+ soup_message_body_complete(message->response_body);
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ } else
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+}
+
+class SecurityPolicyTest: public Test {
+public:
+ MAKE_GLIB_TEST_FIXTURE(SecurityPolicyTest);
+
+ enum SecurityPolicy {
+ Local = 1 << 1,
+ NoAccess = 1 << 2,
+ DisplayIsolated = 1 << 3,
+ Secure = 1 << 4,
+ CORSEnabled = 1 << 5,
+ EmptyDocument = 1 << 6
+ };
+
+ SecurityPolicyTest()
+ : m_manager(webkit_web_context_get_security_manager(m_webContext.get()))
+ {
+ }
+
+ void verifyThatSchemeMatchesPolicy(const char* scheme, unsigned policy)
+ {
+ if (policy & Local)
+ g_assert(webkit_security_manager_uri_scheme_is_local(m_manager, scheme));
+ else
+ g_assert(!webkit_security_manager_uri_scheme_is_local(m_manager, scheme));
+ if (policy & NoAccess)
+ g_assert(webkit_security_manager_uri_scheme_is_no_access(m_manager, scheme));
+ else
+ g_assert(!webkit_security_manager_uri_scheme_is_no_access(m_manager, scheme));
+ if (policy & DisplayIsolated)
+ g_assert(webkit_security_manager_uri_scheme_is_display_isolated(m_manager, scheme));
+ else
+ g_assert(!webkit_security_manager_uri_scheme_is_display_isolated(m_manager, scheme));
+ if (policy & Secure)
+ g_assert(webkit_security_manager_uri_scheme_is_secure(m_manager, scheme));
+ else
+ g_assert(!webkit_security_manager_uri_scheme_is_secure(m_manager, scheme));
+ if (policy & CORSEnabled)
+ g_assert(webkit_security_manager_uri_scheme_is_cors_enabled(m_manager, scheme));
+ else
+ g_assert(!webkit_security_manager_uri_scheme_is_cors_enabled(m_manager, scheme));
+ if (policy & EmptyDocument)
+ g_assert(webkit_security_manager_uri_scheme_is_empty_document(m_manager, scheme));
+ else
+ g_assert(!webkit_security_manager_uri_scheme_is_empty_document(m_manager, scheme));
+ }
+
+ WebKitSecurityManager* m_manager;
+};
+
+static void testWebContextSecurityPolicy(SecurityPolicyTest* test, gconstpointer)
+{
+ // VerifyThatSchemeMatchesPolicy default policy for well known schemes.
+ test->verifyThatSchemeMatchesPolicy("http", SecurityPolicyTest::CORSEnabled);
+ test->verifyThatSchemeMatchesPolicy("https", SecurityPolicyTest::CORSEnabled | SecurityPolicyTest::Secure);
+ test->verifyThatSchemeMatchesPolicy("file", SecurityPolicyTest::Local);
+ test->verifyThatSchemeMatchesPolicy("data", SecurityPolicyTest::NoAccess | SecurityPolicyTest::Secure);
+ test->verifyThatSchemeMatchesPolicy("about", SecurityPolicyTest::NoAccess | SecurityPolicyTest::Secure | SecurityPolicyTest::EmptyDocument);
+
+ // Custom scheme.
+ test->verifyThatSchemeMatchesPolicy("foo", 0);
+
+ webkit_security_manager_register_uri_scheme_as_local(test->m_manager, "foo");
+ test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local);
+ webkit_security_manager_register_uri_scheme_as_no_access(test->m_manager, "foo");
+ test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local | SecurityPolicyTest::NoAccess);
+ webkit_security_manager_register_uri_scheme_as_display_isolated(test->m_manager, "foo");
+ test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local | SecurityPolicyTest::NoAccess | SecurityPolicyTest::DisplayIsolated);
+ webkit_security_manager_register_uri_scheme_as_secure(test->m_manager, "foo");
+ test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local | SecurityPolicyTest::NoAccess | SecurityPolicyTest::DisplayIsolated | SecurityPolicyTest::Secure);
+ webkit_security_manager_register_uri_scheme_as_cors_enabled(test->m_manager, "foo");
+ test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local | SecurityPolicyTest::NoAccess | SecurityPolicyTest::DisplayIsolated | SecurityPolicyTest::Secure
+ | SecurityPolicyTest::CORSEnabled);
+ webkit_security_manager_register_uri_scheme_as_empty_document(test->m_manager, "foo");
+ test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local | SecurityPolicyTest::NoAccess | SecurityPolicyTest::DisplayIsolated | SecurityPolicyTest::Secure
+ | SecurityPolicyTest::CORSEnabled | SecurityPolicyTest::EmptyDocument);
+}
+
+static void testWebContextSecurityFileXHR(WebViewTest* test, gconstpointer)
+{
+ GUniquePtr<char> fileURL(g_strdup_printf("file://%s/simple.html", Test::getResourcesDir(Test::WebKit2Resources).data()));
+ test->loadURI(fileURL.get());
+ test->waitUntilLoadFinished();
+
+ GUniquePtr<char> jsonURL(g_strdup_printf("file://%s/simple.json", Test::getResourcesDir().data()));
+ GUniquePtr<char> xhr(g_strdup_printf("var xhr = new XMLHttpRequest; xhr.open(\"GET\", \"%s\"); xhr.send();", jsonURL.get()));
+
+ // By default file access is not allowed, this will fail with a cross-origin error.
+ GUniqueOutPtr<GError> error;
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(xhr.get(), &error.outPtr());
+ g_assert(!javascriptResult);
+ g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+
+ // Allow file access from file URLs.
+ webkit_settings_set_allow_file_access_from_file_urls(webkit_web_view_get_settings(test->m_webView), TRUE);
+ test->loadURI(fileURL.get());
+ test->waitUntilLoadFinished();
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished(xhr.get(), &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error);
+
+ // It isn't still possible to load file from an HTTP URL.
+ test->loadURI(kServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished(xhr.get(), &error.outPtr());
+ g_assert(!javascriptResult);
+ g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+
+ webkit_settings_set_allow_file_access_from_file_urls(webkit_web_view_get_settings(test->m_webView), FALSE);
+}
+
+void beforeAll()
+{
+ kServer = new WebKitTestServer();
+ kServer->run(serverCallback);
+
+ Test::add("WebKitWebContext", "default-context", testWebContextDefault);
+ WebViewTest::add("WebKitWebContext", "configuration", testWebContextConfiguration);
+ PluginsTest::add("WebKitWebContext", "get-plugins", testWebContextGetPlugins);
+ URISchemeTest::add("WebKitWebContext", "uri-scheme", testWebContextURIScheme);
+ Test::add("WebKitWebContext", "spell-checker", testWebContextSpellChecker);
+ WebViewTest::add("WebKitWebContext", "languages", testWebContextLanguages);
+ SecurityPolicyTest::add("WebKitSecurityManager", "security-policy", testWebContextSecurityPolicy);
+ WebViewTest::add("WebKitSecurityManager", "file-xhr", testWebContextSecurityFileXHR);
+}
+
+void afterAll()
+{
+ delete kServer;
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp
new file mode 100644
index 000000000..e53df6d9d
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp
@@ -0,0 +1,865 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ * Copyright (C) 2014 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebKitTestServer.h"
+#include "WebViewTest.h"
+#include <JavaScriptCore/JSStringRef.h>
+#include <JavaScriptCore/JSValueRef.h>
+#include <glib/gstdio.h>
+#include <wtf/glib/GRefPtr.h>
+
+class IsPlayingAudioWebViewTest : public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(IsPlayingAudioWebViewTest);
+
+ static void isPlayingAudioChanged(GObject*, GParamSpec*, IsPlayingAudioWebViewTest* test)
+ {
+ g_signal_handlers_disconnect_by_func(test->m_webView, reinterpret_cast<void*>(isPlayingAudioChanged), test);
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ void waitUntilIsPlayingAudioChanged()
+ {
+ g_signal_connect(m_webView, "notify::is-playing-audio", G_CALLBACK(isPlayingAudioChanged), this);
+ g_main_loop_run(m_mainLoop);
+ }
+};
+
+static WebKitTestServer* gServer;
+
+static void testWebViewWebContext(WebViewTest* test, gconstpointer)
+{
+ g_assert(webkit_web_view_get_context(test->m_webView) == test->m_webContext.get());
+ g_assert(webkit_web_context_get_default() != test->m_webContext.get());
+
+ // Check that a web view created with g_object_new has the default context.
+ GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, nullptr));
+ g_assert(webkit_web_view_get_context(webView.get()) == webkit_web_context_get_default());
+
+ // Check that a web view created with a related view has the related view context.
+ webView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(test->m_webView));
+ g_assert(webkit_web_view_get_context(webView.get()) == test->m_webContext.get());
+
+ // Check that a web context given as construct parameter is ignored if a related view is also provided.
+ webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+ "web-context", webkit_web_context_get_default(), "related-view", test->m_webView, nullptr));
+ g_assert(webkit_web_view_get_context(webView.get()) == test->m_webContext.get());
+}
+
+static void testWebViewWebContextLifetime(WebViewTest* test, gconstpointer)
+{
+ WebKitWebContext* webContext = webkit_web_context_new();
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webContext));
+
+ GtkWidget* webView = webkit_web_view_new_with_context(webContext);
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView));
+
+ g_object_ref_sink(webView);
+ g_object_unref(webContext);
+
+ // Check that the web view still has a valid context.
+ WebKitWebContext* tmpContext = webkit_web_view_get_context(WEBKIT_WEB_VIEW(webView));
+ g_assert_true(WEBKIT_IS_WEB_CONTEXT(tmpContext));
+ g_object_unref(webView);
+
+ WebKitWebContext* webContext2 = webkit_web_context_new();
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webContext2));
+
+ GtkWidget* webView2 = webkit_web_view_new_with_context(webContext2);
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView2));
+
+ g_object_ref_sink(webView2);
+ g_object_unref(webView2);
+
+ // Check that the context is still valid.
+ g_assert_true(WEBKIT_IS_WEB_CONTEXT(webContext2));
+ g_object_unref(webContext2);
+}
+
+static void testWebViewCustomCharset(WebViewTest* test, gconstpointer)
+{
+ test->loadHtml("<html><body>WebKitGTK+ custom encoding test</body></html>", nullptr);
+ g_assert(!webkit_web_view_get_custom_charset(test->m_webView));
+ webkit_web_view_set_custom_charset(test->m_webView, "utf8");
+ // Changing the charset reloads the page, so wait until reloaded.
+ test->waitUntilLoadFinished();
+ g_assert_cmpstr(webkit_web_view_get_custom_charset(test->m_webView), ==, "utf8");
+
+ // Go back to the default charset and wait until reloaded.
+ webkit_web_view_set_custom_charset(test->m_webView, nullptr);
+ test->waitUntilLoadFinished();
+ g_assert(!webkit_web_view_get_custom_charset(test->m_webView));
+}
+
+static void testWebViewSettings(WebViewTest* test, gconstpointer)
+{
+ WebKitSettings* defaultSettings = webkit_web_view_get_settings(test->m_webView);
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(defaultSettings));
+ g_assert(defaultSettings);
+ g_assert(webkit_settings_get_enable_javascript(defaultSettings));
+
+ GRefPtr<WebKitSettings> newSettings = adoptGRef(webkit_settings_new());
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings.get()));
+ g_object_set(G_OBJECT(newSettings.get()), "enable-javascript", FALSE, NULL);
+ webkit_web_view_set_settings(test->m_webView, newSettings.get());
+
+ WebKitSettings* settings = webkit_web_view_get_settings(test->m_webView);
+ g_assert(settings != defaultSettings);
+ g_assert(!webkit_settings_get_enable_javascript(settings));
+
+ GRefPtr<GtkWidget> webView2 = webkit_web_view_new();
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView2.get()));
+ webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView2.get()), settings);
+ g_assert(webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView2.get())) == settings);
+
+ GRefPtr<WebKitSettings> newSettings2 = adoptGRef(webkit_settings_new());
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings2.get()));
+ webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView2.get()), newSettings2.get());
+ settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView2.get()));
+ g_assert(settings == newSettings2.get());
+ g_assert(webkit_settings_get_enable_javascript(settings));
+
+ GRefPtr<GtkWidget> webView3 = webkit_web_view_new_with_settings(newSettings2.get());
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView3.get()));
+ g_assert(webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView3.get())) == newSettings2.get());
+}
+
+static void testWebViewZoomLevel(WebViewTest* test, gconstpointer)
+{
+ g_assert_cmpfloat(webkit_web_view_get_zoom_level(test->m_webView), ==, 1);
+ webkit_web_view_set_zoom_level(test->m_webView, 2.5);
+ g_assert_cmpfloat(webkit_web_view_get_zoom_level(test->m_webView), ==, 2.5);
+
+ webkit_settings_set_zoom_text_only(webkit_web_view_get_settings(test->m_webView), TRUE);
+ // The zoom level shouldn't change when zoom-text-only setting changes.
+ g_assert_cmpfloat(webkit_web_view_get_zoom_level(test->m_webView), ==, 2.5);
+}
+
+static void testWebViewRunJavaScript(WebViewTest* test, gconstpointer)
+{
+ static const char* html = "<html><body><a id='WebKitLink' href='http://www.webkitgtk.org/' title='WebKitGTK+ Title'>WebKitGTK+ Website</a></body></html>";
+ test->loadHtml(html, 0);
+ test->waitUntilLoadFinished();
+
+ GUniqueOutPtr<GError> error;
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.getElementById('WebKitLink').title;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "WebKitGTK+ Title");
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.getElementById('WebKitLink').href;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "http://www.webkitgtk.org/");
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.getElementById('WebKitLink').textContent", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "WebKitGTK+ Website");
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = 25;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 25);
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = 2.5;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 2.5);
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = true", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ g_assert(WebViewTest::javascriptResultToBoolean(javascriptResult));
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = false", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ g_assert(!WebViewTest::javascriptResultToBoolean(javascriptResult));
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = null", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ g_assert(WebViewTest::javascriptResultIsNull(javascriptResult));
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("function Foo() { a = 25; } Foo();", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ g_assert(WebViewTest::javascriptResultIsUndefined(javascriptResult));
+
+ javascriptResult = test->runJavaScriptFromGResourceAndWaitUntilFinished("/org/webkit/webkit2gtk/tests/link-title.js", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "WebKitGTK+ Title");
+
+ javascriptResult = test->runJavaScriptFromGResourceAndWaitUntilFinished("/wrong/path/to/resource.js", &error.outPtr());
+ g_assert(!javascriptResult);
+ g_assert_error(error.get(), G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND);
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("foo();", &error.outPtr());
+ g_assert(!javascriptResult);
+ g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+}
+
+class FullScreenClientTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(FullScreenClientTest);
+
+ enum FullScreenEvent {
+ None,
+ Enter,
+ Leave
+ };
+
+ static gboolean viewEnterFullScreenCallback(WebKitWebView*, FullScreenClientTest* test)
+ {
+ test->m_event = Enter;
+ g_main_loop_quit(test->m_mainLoop);
+ return FALSE;
+ }
+
+ static gboolean viewLeaveFullScreenCallback(WebKitWebView*, FullScreenClientTest* test)
+ {
+ test->m_event = Leave;
+ g_main_loop_quit(test->m_mainLoop);
+ return FALSE;
+ }
+
+ FullScreenClientTest()
+ : m_event(None)
+ {
+ webkit_settings_set_enable_fullscreen(webkit_web_view_get_settings(m_webView), TRUE);
+ g_signal_connect(m_webView, "enter-fullscreen", G_CALLBACK(viewEnterFullScreenCallback), this);
+ g_signal_connect(m_webView, "leave-fullscreen", G_CALLBACK(viewLeaveFullScreenCallback), this);
+ }
+
+ ~FullScreenClientTest()
+ {
+ g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ void requestFullScreenAndWaitUntilEnteredFullScreen()
+ {
+ m_event = None;
+ webkit_web_view_run_javascript(m_webView, "document.documentElement.webkitRequestFullScreen();", 0, 0, 0);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ static gboolean leaveFullScreenIdle(FullScreenClientTest* test)
+ {
+ test->keyStroke(GDK_KEY_Escape);
+ return FALSE;
+ }
+
+ void leaveFullScreenAndWaitUntilLeftFullScreen()
+ {
+ m_event = None;
+ g_idle_add(reinterpret_cast<GSourceFunc>(leaveFullScreenIdle), this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ FullScreenEvent m_event;
+};
+
+static void testWebViewFullScreen(FullScreenClientTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+ test->loadHtml("<html><body>FullScreen test</body></html>", 0);
+ test->waitUntilLoadFinished();
+ test->requestFullScreenAndWaitUntilEnteredFullScreen();
+ g_assert_cmpint(test->m_event, ==, FullScreenClientTest::Enter);
+ test->leaveFullScreenAndWaitUntilLeftFullScreen();
+ g_assert_cmpint(test->m_event, ==, FullScreenClientTest::Leave);
+}
+
+static void testWebViewCanShowMIMEType(WebViewTest* test, gconstpointer)
+{
+ // Supported MIME types.
+ g_assert(webkit_web_view_can_show_mime_type(test->m_webView, "text/html"));
+ g_assert(webkit_web_view_can_show_mime_type(test->m_webView, "text/plain"));
+ g_assert(webkit_web_view_can_show_mime_type(test->m_webView, "image/jpeg"));
+
+ // Unsupported MIME types.
+ g_assert(!webkit_web_view_can_show_mime_type(test->m_webView, "text/vcard"));
+ g_assert(!webkit_web_view_can_show_mime_type(test->m_webView, "application/zip"));
+ g_assert(!webkit_web_view_can_show_mime_type(test->m_webView, "application/octet-stream"));
+
+ // Plugins are only supported when enabled.
+ webkit_web_context_set_additional_plugins_directory(webkit_web_view_get_context(test->m_webView), WEBKIT_TEST_PLUGIN_DIR);
+ g_assert(webkit_web_view_can_show_mime_type(test->m_webView, "application/x-webkit-test-netscape"));
+ webkit_settings_set_enable_plugins(webkit_web_view_get_settings(test->m_webView), FALSE);
+ g_assert(!webkit_web_view_can_show_mime_type(test->m_webView, "application/x-webkit-test-netscape"));
+}
+
+class FormClientTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(FormClientTest);
+
+ static void submitFormCallback(WebKitWebView*, WebKitFormSubmissionRequest* request, FormClientTest* test)
+ {
+ test->submitForm(request);
+ }
+
+ FormClientTest()
+ : m_submitPositionX(0)
+ , m_submitPositionY(0)
+ {
+ g_signal_connect(m_webView, "submit-form", G_CALLBACK(submitFormCallback), this);
+ }
+
+ ~FormClientTest()
+ {
+ g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ }
+
+ void submitForm(WebKitFormSubmissionRequest* request)
+ {
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
+ m_request = request;
+ webkit_form_submission_request_submit(request);
+ quitMainLoop();
+ }
+
+ GHashTable* waitUntilFormSubmittedAndGetTextFields()
+ {
+ g_main_loop_run(m_mainLoop);
+ return webkit_form_submission_request_get_text_fields(m_request.get());
+ }
+
+ static gboolean doClickIdleCallback(FormClientTest* test)
+ {
+ test->clickMouseButton(test->m_submitPositionX, test->m_submitPositionY, 1);
+ return FALSE;
+ }
+
+ void submitFormAtPosition(int x, int y)
+ {
+ m_submitPositionX = x;
+ m_submitPositionY = y;
+ g_idle_add(reinterpret_cast<GSourceFunc>(doClickIdleCallback), this);
+ }
+
+ int m_submitPositionX;
+ int m_submitPositionY;
+ GRefPtr<WebKitFormSubmissionRequest> m_request;
+};
+
+static void testWebViewSubmitForm(FormClientTest* test, gconstpointer)
+{
+ test->showInWindowAndWaitUntilMapped();
+
+ const char* formHTML =
+ "<html><body>"
+ " <form action='#'>"
+ " <input type='text' name='text1' value='value1'>"
+ " <input type='text' name='text2' value='value2'>"
+ " <input type='password' name='password' value='secret'>"
+ " <textarea cols='5' rows='5' name='textarea'>Text</textarea>"
+ " <input type='hidden' name='hidden1' value='hidden1'>"
+ " <input type='submit' value='Submit' style='position:absolute; left:1; top:1' size='10'>"
+ " </form>"
+ "</body></html>";
+
+ test->loadHtml(formHTML, "file:///");
+ test->waitUntilLoadFinished();
+
+ test->submitFormAtPosition(5, 5);
+ GHashTable* values = test->waitUntilFormSubmittedAndGetTextFields();
+ g_assert(values);
+ g_assert_cmpuint(g_hash_table_size(values), ==, 3);
+ g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(values, "text1")), ==, "value1");
+ g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(values, "text2")), ==, "value2");
+ g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(values, "password")), ==, "secret");
+}
+
+class SaveWebViewTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(SaveWebViewTest);
+
+ SaveWebViewTest()
+ : m_tempDirectory(g_dir_make_tmp("WebKit2SaveViewTest-XXXXXX", 0))
+ {
+ }
+
+ ~SaveWebViewTest()
+ {
+ if (G_IS_FILE(m_file.get()))
+ g_file_delete(m_file.get(), 0, 0);
+
+ if (G_IS_INPUT_STREAM(m_inputStream.get()))
+ g_input_stream_close(m_inputStream.get(), 0, 0);
+
+ if (m_tempDirectory)
+ g_rmdir(m_tempDirectory.get());
+ }
+
+ static void webViewSavedToStreamCallback(GObject* object, GAsyncResult* result, SaveWebViewTest* test)
+ {
+ GUniqueOutPtr<GError> error;
+ test->m_inputStream = adoptGRef(webkit_web_view_save_finish(test->m_webView, result, &error.outPtr()));
+ g_assert(G_IS_INPUT_STREAM(test->m_inputStream.get()));
+ g_assert(!error);
+
+ test->quitMainLoop();
+ }
+
+ static void webViewSavedToFileCallback(GObject* object, GAsyncResult* result, SaveWebViewTest* test)
+ {
+ GUniqueOutPtr<GError> error;
+ g_assert(webkit_web_view_save_to_file_finish(test->m_webView, result, &error.outPtr()));
+ g_assert(!error);
+
+ test->quitMainLoop();
+ }
+
+ void saveAndWaitForStream()
+ {
+ webkit_web_view_save(m_webView, WEBKIT_SAVE_MODE_MHTML, 0, reinterpret_cast<GAsyncReadyCallback>(webViewSavedToStreamCallback), this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ void saveAndWaitForFile()
+ {
+ m_saveDestinationFilePath.reset(g_build_filename(m_tempDirectory.get(), "testWebViewSaveResult.mht", NULL));
+ m_file = adoptGRef(g_file_new_for_path(m_saveDestinationFilePath.get()));
+ webkit_web_view_save_to_file(m_webView, m_file.get(), WEBKIT_SAVE_MODE_MHTML, 0, reinterpret_cast<GAsyncReadyCallback>(webViewSavedToFileCallback), this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ GUniquePtr<char> m_tempDirectory;
+ GUniquePtr<char> m_saveDestinationFilePath;
+ GRefPtr<GInputStream> m_inputStream;
+ GRefPtr<GFile> m_file;
+};
+
+static void testWebViewSave(SaveWebViewTest* test, gconstpointer)
+{
+ test->loadHtml("<html>"
+ "<body>"
+ " <p>A paragraph with plain text</p>"
+ " <p>"
+ " A red box: <img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AYWDTMVwnSZnwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFklEQVQI12P8z8DAwMDAxMDAwMDAAAANHQEDK+mmyAAAAABJRU5ErkJggg=='></br>"
+ " A blue box: <img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AYWDTMvBHhALQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFklEQVQI12Nk4PnPwMDAxMDAwMDAAAALrwEPPIs1pgAAAABJRU5ErkJggg=='>"
+ " </p>"
+ "</body>"
+ "</html>", 0);
+ test->waitUntilLoadFinished();
+
+ // Write to a file and to an input stream.
+ test->saveAndWaitForFile();
+ test->saveAndWaitForStream();
+
+ // We should have exactly the same amount of bytes in the file
+ // than those coming from the GInputStream. We don't compare the
+ // strings read since the 'Date' field and the boundaries will be
+ // different on each case. MHTML functionality will be tested by
+ // Layout tests, so checking the amount of bytes is enough.
+ GUniqueOutPtr<GError> error;
+ gchar buffer[512] = { 0 };
+ gssize readBytes = 0;
+ gssize totalBytesFromStream = 0;
+ while ((readBytes = g_input_stream_read(test->m_inputStream.get(), &buffer, 512, 0, &error.outPtr()))) {
+ g_assert(!error);
+ totalBytesFromStream += readBytes;
+ }
+
+ // Check that the file exists and that it contains the same amount of bytes.
+ GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(test->m_file.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0));
+ g_assert_cmpint(g_file_info_get_size(fileInfo.get()), ==, totalBytesFromStream);
+}
+
+// To test page visibility API. Currently only 'visible', 'hidden' and 'prerender' states are implemented fully in WebCore.
+// See also http://www.w3.org/TR/2011/WD-page-visibility-20110602/ and https://developers.google.com/chrome/whitepapers/pagevisibility
+static void testWebViewPageVisibility(WebViewTest* test, gconstpointer)
+{
+ test->loadHtml("<html><title></title>"
+ "<body><p>Test Web Page Visibility</p>"
+ "<script>"
+ "document.addEventListener(\"visibilitychange\", onVisibilityChange, false);"
+ "function onVisibilityChange() {"
+ " document.title = document.visibilityState;"
+ "}"
+ "</script>"
+ "</body></html>",
+ 0);
+
+ // Wait until the page is loaded. Initial visibility should be 'prerender'.
+ test->waitUntilLoadFinished();
+
+ GUniqueOutPtr<GError> error;
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.visibilityState;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "prerender");
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.hidden;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ g_assert(WebViewTest::javascriptResultToBoolean(javascriptResult));
+
+ // Show the page. The visibility should be updated to 'visible'.
+ test->showInWindow();
+ test->waitUntilTitleChanged();
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.visibilityState;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "visible");
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.hidden;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ g_assert(!WebViewTest::javascriptResultToBoolean(javascriptResult));
+
+ // Hide the page. The visibility should be updated to 'hidden'.
+ gtk_widget_hide(GTK_WIDGET(test->m_webView));
+ test->waitUntilTitleChanged();
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.visibilityState;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(valueString.get(), ==, "hidden");
+
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.hidden;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error.get());
+ g_assert(WebViewTest::javascriptResultToBoolean(javascriptResult));
+}
+
+class SnapshotWebViewTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(SnapshotWebViewTest);
+
+ static void onSnapshotCancelledReady(WebKitWebView* web_view, GAsyncResult* res, SnapshotWebViewTest* test)
+ {
+ GUniqueOutPtr<GError> error;
+ test->m_surface = webkit_web_view_get_snapshot_finish(web_view, res, &error.outPtr());
+ g_assert(!test->m_surface);
+ g_assert_error(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ test->quitMainLoop();
+ }
+
+ gboolean getSnapshotAndCancel()
+ {
+ if (m_surface)
+ cairo_surface_destroy(m_surface);
+ m_surface = 0;
+ GRefPtr<GCancellable> cancellable = adoptGRef(g_cancellable_new());
+ webkit_web_view_get_snapshot(m_webView, WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE, cancellable.get(), reinterpret_cast<GAsyncReadyCallback>(onSnapshotCancelledReady), this);
+ g_cancellable_cancel(cancellable.get());
+ g_main_loop_run(m_mainLoop);
+
+ return true;
+ }
+
+};
+
+static void testWebViewSnapshot(SnapshotWebViewTest* test, gconstpointer)
+{
+ test->loadHtml("<html><head><style>html { width: 200px; height: 100px; } ::-webkit-scrollbar { display: none; }</style></head><body><p>Whatever</p></body></html>", nullptr);
+ test->waitUntilLoadFinished();
+
+ // WEBKIT_SNAPSHOT_REGION_VISIBLE returns a null surface when the view is not visible.
+ cairo_surface_t* surface1 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE);
+ g_assert(!surface1);
+
+ // WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT works even if the window is not visible.
+ surface1 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT, WEBKIT_SNAPSHOT_OPTIONS_NONE);
+ g_assert(surface1);
+ g_assert_cmpuint(cairo_surface_get_type(surface1), ==, CAIRO_SURFACE_TYPE_IMAGE);
+ g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, 200);
+ g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, 100);
+
+ // Show the WebView in a popup widow of 50x50 and try again with WEBKIT_SNAPSHOT_REGION_VISIBLE.
+ test->showInWindowAndWaitUntilMapped(GTK_WINDOW_POPUP, 50, 50);
+ surface1 = cairo_surface_reference(test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE));
+ g_assert(surface1);
+ g_assert_cmpuint(cairo_surface_get_type(surface1), ==, CAIRO_SURFACE_TYPE_IMAGE);
+ g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, 50);
+ g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, 50);
+
+ // Select all text in the WebView, request a snapshot ignoring selection.
+ test->selectAll();
+ cairo_surface_t* surface2 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE);
+ g_assert(surface2);
+ g_assert(Test::cairoSurfacesEqual(surface1, surface2));
+
+ // Request a new snapshot, including the selection this time. The size should be the same but the result
+ // must be different to the one previously obtained.
+ surface2 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_INCLUDE_SELECTION_HIGHLIGHTING);
+ g_assert_cmpuint(cairo_surface_get_type(surface2), ==, CAIRO_SURFACE_TYPE_IMAGE);
+ g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, cairo_image_surface_get_width(surface2));
+ g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, cairo_image_surface_get_height(surface2));
+ g_assert(!Test::cairoSurfacesEqual(surface1, surface2));
+
+ // Get a snpashot with a transparent background, the result must be different.
+ surface2 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_TRANSPARENT_BACKGROUND);
+ g_assert_cmpuint(cairo_surface_get_type(surface2), ==, CAIRO_SURFACE_TYPE_IMAGE);
+ g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, cairo_image_surface_get_width(surface2));
+ g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, cairo_image_surface_get_height(surface2));
+ g_assert(!Test::cairoSurfacesEqual(surface1, surface2));
+ cairo_surface_destroy(surface1);
+
+ // Test that cancellation works.
+ g_assert(test->getSnapshotAndCancel());
+}
+
+class NotificationWebViewTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(NotificationWebViewTest);
+
+ enum NotificationEvent {
+ None,
+ Permission,
+ Shown,
+ Closed,
+ OnClosed,
+ };
+
+ static gboolean permissionRequestCallback(WebKitWebView*, WebKitPermissionRequest *request, NotificationWebViewTest* test)
+ {
+ g_assert(WEBKIT_IS_NOTIFICATION_PERMISSION_REQUEST(request));
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
+
+ test->m_event = Permission;
+
+ webkit_permission_request_allow(request);
+
+ g_main_loop_quit(test->m_mainLoop);
+
+ return TRUE;
+ }
+
+ static gboolean notificationClosedCallback(WebKitNotification* notification, NotificationWebViewTest* test)
+ {
+ g_assert(test->m_notification == notification);
+ test->m_notification = nullptr;
+ test->m_event = Closed;
+ if (g_main_loop_is_running(test->m_mainLoop))
+ g_main_loop_quit(test->m_mainLoop);
+ return TRUE;
+ }
+
+ static gboolean showNotificationCallback(WebKitWebView*, WebKitNotification* notification, NotificationWebViewTest* test)
+ {
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(notification));
+ test->m_notification = notification;
+ g_signal_connect(notification, "closed", G_CALLBACK(notificationClosedCallback), test);
+ test->m_event = Shown;
+ g_main_loop_quit(test->m_mainLoop);
+ return TRUE;
+ }
+
+ static void notificationsMessageReceivedCallback(WebKitUserContentManager* userContentManager, WebKitJavascriptResult*, NotificationWebViewTest* test)
+ {
+ test->m_event = OnClosed;
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ NotificationWebViewTest()
+ : WebViewTest(webkit_user_content_manager_new())
+ , m_notification(nullptr)
+ , m_event(None)
+ {
+ g_signal_connect(m_webView, "permission-request", G_CALLBACK(permissionRequestCallback), this);
+ g_signal_connect(m_webView, "show-notification", G_CALLBACK(showNotificationCallback), this);
+ WebKitUserContentManager* manager = webkit_web_view_get_user_content_manager(m_webView);
+ webkit_user_content_manager_register_script_message_handler(manager, "notifications");
+ g_signal_connect(manager, "script-message-received::notifications", G_CALLBACK(notificationsMessageReceivedCallback), this);
+ }
+
+ ~NotificationWebViewTest()
+ {
+ g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ WebKitUserContentManager* manager = webkit_web_view_get_user_content_manager(m_webView);
+ g_signal_handlers_disconnect_matched(manager, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ webkit_user_content_manager_unregister_script_message_handler(manager, "notifications");
+ }
+
+ void requestPermissionAndWaitUntilGiven()
+ {
+ m_event = None;
+ webkit_web_view_run_javascript(m_webView, "Notification.requestPermission();", nullptr, nullptr, nullptr);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ void requestNotificationAndWaitUntilShown(const char* title, const char* body)
+ {
+ m_event = None;
+
+ GUniquePtr<char> jscode(g_strdup_printf("n = new Notification('%s', { body: '%s'});", title, body));
+ webkit_web_view_run_javascript(m_webView, jscode.get(), nullptr, nullptr, nullptr);
+
+ g_main_loop_run(m_mainLoop);
+ }
+
+ void closeNotificationAndWaitUntilClosed()
+ {
+ m_event = None;
+ webkit_web_view_run_javascript(m_webView, "n.close()", nullptr, nullptr, nullptr);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ void closeNotificationAndWaitUntilOnClosed()
+ {
+ g_assert(m_notification);
+ m_event = None;
+ runJavaScriptAndWaitUntilFinished("n.onclose = function() { window.webkit.messageHandlers.notifications.postMessage('closed'); }", nullptr);
+ webkit_notification_close(m_notification);
+ g_assert(m_event == Closed);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ NotificationEvent m_event;
+ WebKitNotification* m_notification;
+};
+
+static void testWebViewNotification(NotificationWebViewTest* test, gconstpointer)
+{
+ // Notifications don't work with local or special schemes.
+ test->loadURI(gServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+
+ test->requestPermissionAndWaitUntilGiven();
+ g_assert(test->m_event == NotificationWebViewTest::Permission);
+
+ static const char* title = "This is a notification";
+ static const char* body = "This is the body.";
+ test->requestNotificationAndWaitUntilShown(title, body);
+
+ g_assert(test->m_event == NotificationWebViewTest::Shown);
+ g_assert(test->m_notification);
+ g_assert_cmpstr(webkit_notification_get_title(test->m_notification), ==, title);
+ g_assert_cmpstr(webkit_notification_get_body(test->m_notification), ==, body);
+
+ test->closeNotificationAndWaitUntilClosed();
+ g_assert(test->m_event == NotificationWebViewTest::Closed);
+
+ test->requestNotificationAndWaitUntilShown(title, body);
+ g_assert(test->m_event == NotificationWebViewTest::Shown);
+
+ test->closeNotificationAndWaitUntilOnClosed();
+ g_assert(test->m_event == NotificationWebViewTest::OnClosed);
+
+ test->requestNotificationAndWaitUntilShown(title, body);
+ g_assert(test->m_event == NotificationWebViewTest::Shown);
+
+ test->loadURI(gServer->getURIForPath("/").data());
+ test->waitUntilLoadFinished();
+ g_assert(test->m_event == NotificationWebViewTest::Closed);
+}
+
+static void testWebViewIsPlayingAudio(IsPlayingAudioWebViewTest* test, gconstpointer)
+{
+ // The web view must be realized for the video to start playback and
+ // trigger changes in WebKitWebView::is-playing-audio.
+ test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
+
+ // Initially, web views should always report no audio being played.
+ g_assert(!webkit_web_view_is_playing_audio(test->m_webView));
+
+ GUniquePtr<char> resourcePath(g_build_filename(Test::getResourcesDir(Test::WebKit2Resources).data(), "file-with-video.html", nullptr));
+ GUniquePtr<char> resourceURL(g_filename_to_uri(resourcePath.get(), nullptr, nullptr));
+ webkit_web_view_load_uri(test->m_webView, resourceURL.get());
+ test->waitUntilLoadFinished();
+ g_assert(!webkit_web_view_is_playing_audio(test->m_webView));
+
+ webkit_web_view_run_javascript(test->m_webView, "playVideo();", nullptr, nullptr, nullptr);
+ test->waitUntilIsPlayingAudioChanged();
+ g_assert(webkit_web_view_is_playing_audio(test->m_webView));
+
+ // Pause the video, and check again.
+ webkit_web_view_run_javascript(test->m_webView, "document.getElementById('test-video').pause();", nullptr, nullptr, nullptr);
+ test->waitUntilIsPlayingAudioChanged();
+ g_assert(!webkit_web_view_is_playing_audio(test->m_webView));
+}
+
+static void testWebViewBackgroundColor(WebViewTest* test, gconstpointer)
+{
+ // White is the default background.
+ GdkRGBA rgba;
+ webkit_web_view_get_background_color(test->m_webView, &rgba);
+ g_assert_cmpfloat(rgba.red, ==, 1);
+ g_assert_cmpfloat(rgba.green, ==, 1);
+ g_assert_cmpfloat(rgba.blue, ==, 1);
+ g_assert_cmpfloat(rgba.alpha, ==, 1);
+
+ // Set a different (semi-transparent red).
+ rgba.red = 1;
+ rgba.green = 0;
+ rgba.blue = 0;
+ rgba.alpha = 0.5;
+ webkit_web_view_set_background_color(test->m_webView, &rgba);
+ g_assert_cmpfloat(rgba.red, ==, 1);
+ g_assert_cmpfloat(rgba.green, ==, 0);
+ g_assert_cmpfloat(rgba.blue, ==, 0);
+ g_assert_cmpfloat(rgba.alpha, ==, 0.5);
+
+ // The actual rendering can't be tested using unit tests, use
+ // MiniBrowser --bg-color="<color-value>" for manually testing this API.
+}
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+ if (message->method != SOUP_METHOD_GET) {
+ soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (g_str_equal(path, "/")) {
+ soup_message_set_status(message, SOUP_STATUS_OK);
+ soup_message_body_complete(message->response_body);
+ } else
+ soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+}
+
+void beforeAll()
+{
+ gServer = new WebKitTestServer();
+ gServer->run(serverCallback);
+
+ WebViewTest::add("WebKitWebView", "web-context", testWebViewWebContext);
+ WebViewTest::add("WebKitWebView", "web-context-lifetime", testWebViewWebContextLifetime);
+ WebViewTest::add("WebKitWebView", "custom-charset", testWebViewCustomCharset);
+ WebViewTest::add("WebKitWebView", "settings", testWebViewSettings);
+ WebViewTest::add("WebKitWebView", "zoom-level", testWebViewZoomLevel);
+ WebViewTest::add("WebKitWebView", "run-javascript", testWebViewRunJavaScript);
+ FullScreenClientTest::add("WebKitWebView", "fullscreen", testWebViewFullScreen);
+ WebViewTest::add("WebKitWebView", "can-show-mime-type", testWebViewCanShowMIMEType);
+ FormClientTest::add("WebKitWebView", "submit-form", testWebViewSubmitForm);
+ SaveWebViewTest::add("WebKitWebView", "save", testWebViewSave);
+ SnapshotWebViewTest::add("WebKitWebView", "snapshot", testWebViewSnapshot);
+ WebViewTest::add("WebKitWebView", "page-visibility", testWebViewPageVisibility);
+ NotificationWebViewTest::add("WebKitWebView", "notification", testWebViewNotification);
+ IsPlayingAudioWebViewTest::add("WebKitWebView", "is-playing-audio", testWebViewIsPlayingAudio);
+ WebViewTest::add("WebKitWebView", "background-color", testWebViewBackgroundColor);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebViewEditor.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebViewEditor.cpp
new file mode 100644
index 000000000..d20c7944f
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebViewEditor.cpp
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebViewTest.h"
+#include <wtf/glib/GRefPtr.h>
+
+class EditorTest: public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(EditorTest);
+
+ static const unsigned kClipboardWaitTimeout = 50;
+ static const unsigned kClipboardWaitMaxTries = 2;
+
+ EditorTest()
+ : m_clipboard(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD))
+ , m_canExecuteEditingCommand(false)
+ , m_triesCount(0)
+ , m_editorState(nullptr)
+ {
+ gtk_clipboard_clear(m_clipboard);
+ }
+
+ static void canExecuteEditingCommandReadyCallback(GObject*, GAsyncResult* result, EditorTest* test)
+ {
+ GUniqueOutPtr<GError> error;
+ test->m_canExecuteEditingCommand = webkit_web_view_can_execute_editing_command_finish(test->m_webView, result, &error.outPtr());
+ g_assert(!error.get());
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ bool canExecuteEditingCommand(const char* command)
+ {
+ m_canExecuteEditingCommand = false;
+ webkit_web_view_can_execute_editing_command(m_webView, command, 0, reinterpret_cast<GAsyncReadyCallback>(canExecuteEditingCommandReadyCallback), this);
+ g_main_loop_run(m_mainLoop);
+ return m_canExecuteEditingCommand;
+ }
+
+ static gboolean waitForClipboardText(EditorTest* test)
+ {
+ test->m_triesCount++;
+ if (gtk_clipboard_wait_is_text_available(test->m_clipboard) || test->m_triesCount > kClipboardWaitMaxTries) {
+ g_main_loop_quit(test->m_mainLoop);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ void copyClipboard()
+ {
+ webkit_web_view_execute_editing_command(m_webView, WEBKIT_EDITING_COMMAND_COPY);
+ // There's no way to know when the selection has been copied to
+ // the clipboard, so use a timeout source to query the clipboard.
+ m_triesCount = 0;
+ g_timeout_add(kClipboardWaitTimeout, reinterpret_cast<GSourceFunc>(waitForClipboardText), this);
+ g_main_loop_run(m_mainLoop);
+ }
+
+ gchar* cutSelection()
+ {
+ g_assert(canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT));
+ g_assert(canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE));
+
+ webkit_web_view_execute_editing_command(m_webView, WEBKIT_EDITING_COMMAND_CUT);
+ // There's no way to know when the selection has been cut to
+ // the clipboard, so use a timeout source to query the clipboard.
+ m_triesCount = 0;
+ g_timeout_add(kClipboardWaitTimeout, reinterpret_cast<GSourceFunc>(waitForClipboardText), this);
+ g_main_loop_run(m_mainLoop);
+
+ return gtk_clipboard_wait_for_text(m_clipboard);
+ }
+
+ WebKitEditorState* editorState()
+ {
+ if (m_editorState)
+ return m_editorState;
+
+ m_editorState = webkit_web_view_get_editor_state(m_webView);
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_editorState));
+ return m_editorState;
+ }
+
+ static void quitMainLoopInCallback(EditorTest* test)
+ {
+ g_main_loop_quit(test->m_mainLoop);
+ }
+
+ unsigned typingAttributes()
+ {
+ return webkit_editor_state_get_typing_attributes(editorState());
+ }
+
+ unsigned waitUntilTypingAttributesChanged()
+ {
+ unsigned long handlerID = g_signal_connect_swapped(editorState(), "notify::typing-attributes", G_CALLBACK(quitMainLoopInCallback), this);
+ g_main_loop_run(m_mainLoop);
+ g_signal_handler_disconnect(m_editorState, handlerID);
+ return typingAttributes();
+ }
+
+ GtkClipboard* m_clipboard;
+ bool m_canExecuteEditingCommand;
+ size_t m_triesCount;
+ WebKitEditorState* m_editorState;
+};
+
+static const char* selectedSpanHTMLFormat =
+ "<html><body contentEditable=\"%s\">"
+ "<span id=\"mainspan\">All work and no play <span id=\"subspan\">make Jack a dull</span> boy.</span>"
+ "<script>document.getSelection().collapse();\n"
+ "document.getSelection().selectAllChildren(document.getElementById('subspan'));\n"
+ "</script></body></html>";
+
+static void testWebViewEditorCutCopyPasteNonEditable(EditorTest* test, gconstpointer)
+{
+ // Nothing loaded yet.
+ g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT));
+ g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_COPY));
+ g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE));
+
+ GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, "false"));
+ test->loadHtml(selectedSpanHTML.get(), nullptr);
+ test->waitUntilLoadFinished();
+
+ g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_COPY));
+ // It's not possible to cut and paste when content is not editable
+ // even if there's a selection.
+ g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT));
+ g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE));
+
+ test->copyClipboard();
+ GUniquePtr<char> clipboardText(gtk_clipboard_wait_for_text(test->m_clipboard));
+ g_assert_cmpstr(clipboardText.get(), ==, "make Jack a dull");
+}
+
+static void testWebViewEditorCutCopyPasteEditable(EditorTest* test, gconstpointer)
+{
+ // Nothing loaded yet.
+ g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT));
+ g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_COPY));
+ g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE));
+
+ g_assert(!test->isEditable());
+ test->setEditable(true);
+ g_assert(test->isEditable());
+
+ GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, "false"));
+ test->loadHtml(selectedSpanHTML.get(), nullptr);
+ test->waitUntilLoadFinished();
+
+ // There's a selection.
+ g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT));
+ g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_COPY));
+ g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE));
+
+ test->copyClipboard();
+ GUniquePtr<char> clipboardText(gtk_clipboard_wait_for_text(test->m_clipboard));
+ g_assert_cmpstr(clipboardText.get(), ==, "make Jack a dull");
+}
+
+static void testWebViewEditorSelectAllNonEditable(EditorTest* test, gconstpointer)
+{
+ g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_SELECT_ALL));
+
+ GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, "false"));
+ test->loadHtml(selectedSpanHTML.get(), nullptr);
+ test->waitUntilLoadFinished();
+
+ g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_SELECT_ALL));
+
+ test->copyClipboard();
+ GUniquePtr<char> clipboardText(gtk_clipboard_wait_for_text(test->m_clipboard));
+
+ // Initially only the subspan is selected.
+ g_assert_cmpstr(clipboardText.get(), ==, "make Jack a dull");
+
+ webkit_web_view_execute_editing_command(test->m_webView, WEBKIT_EDITING_COMMAND_SELECT_ALL);
+ test->copyClipboard();
+ clipboardText.reset(gtk_clipboard_wait_for_text(test->m_clipboard));
+
+ // The mainspan should be selected after calling SELECT_ALL.
+ g_assert_cmpstr(clipboardText.get(), ==, "All work and no play make Jack a dull boy.");
+}
+
+static void testWebViewEditorSelectAllEditable(EditorTest* test, gconstpointer)
+{
+ g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_SELECT_ALL));
+
+ g_assert(!test->isEditable());
+ test->setEditable(true);
+ g_assert(test->isEditable());
+
+ GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, "false"));
+ test->loadHtml(selectedSpanHTML.get(), nullptr);
+ test->waitUntilLoadFinished();
+
+ g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_SELECT_ALL));
+
+ test->copyClipboard();
+ GUniquePtr<char> clipboardText(gtk_clipboard_wait_for_text(test->m_clipboard));
+
+ // Initially only the subspan is selected.
+ g_assert_cmpstr(clipboardText.get(), ==, "make Jack a dull");
+
+ webkit_web_view_execute_editing_command(test->m_webView, WEBKIT_EDITING_COMMAND_SELECT_ALL);
+ test->copyClipboard();
+ clipboardText.reset(gtk_clipboard_wait_for_text(test->m_clipboard));
+
+ // The mainspan should be selected after calling SELECT_ALL.
+ g_assert_cmpstr(clipboardText.get(), ==, "All work and no play make Jack a dull boy.");
+}
+
+static void loadContentsAndTryToCutSelection(EditorTest* test, bool contentEditable)
+{
+ // View is not editable by default.
+ g_assert(!test->isEditable());
+
+ GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, contentEditable ? "true" : "false"));
+ test->loadHtml(selectedSpanHTML.get(), nullptr);
+ test->waitUntilLoadFinished();
+
+ g_assert(!test->isEditable());
+ test->setEditable(true);
+ g_assert(test->isEditable());
+
+ // Cut the selection to the clipboard to see if the view is indeed editable.
+ GUniquePtr<char> clipboardText(test->cutSelection());
+ g_assert_cmpstr(clipboardText.get(), ==, "make Jack a dull");
+
+ // Reset the editable for next test.
+ test->setEditable(false);
+ g_assert(!test->isEditable());
+}
+
+static void testWebViewEditorNonEditable(EditorTest* test)
+{
+ GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, "false"));
+ test->loadHtml(selectedSpanHTML.get(), nullptr);
+ test->waitUntilLoadFinished();
+
+ g_assert(!test->isEditable());
+ test->setEditable(true);
+ g_assert(test->isEditable());
+ test->setEditable(false);
+ g_assert(!test->isEditable());
+
+ // Check if view is indeed non-editable.
+ g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT));
+ g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE));
+}
+
+static void testWebViewEditorEditable(EditorTest* test, gconstpointer)
+{
+ testWebViewEditorNonEditable(test);
+
+ // Reset the editable for next test.
+ test->setEditable(false);
+ g_assert(!test->isEditable());
+
+ loadContentsAndTryToCutSelection(test, true);
+
+ // Reset the editable for next test.
+ test->setEditable(false);
+ g_assert(!test->isEditable());
+
+ loadContentsAndTryToCutSelection(test, false);
+}
+
+static void testWebViewEditorEditorStateTypingAttributes(EditorTest* test, gconstpointer)
+{
+ static const char* typingAttributesHTML =
+ "<html><body>"
+ "normal <b>bold </b><i>italic </i><u>underline </u><strike>strike </strike>"
+ "<b><i>boldanditalic </i></b>"
+ "</body></html>";
+
+ test->loadHtml(typingAttributesHTML, nullptr);
+ test->waitUntilLoadFinished();
+ test->setEditable(true);
+
+ unsigned typingAttributes = test->typingAttributes();
+ g_assert_cmpuint(typingAttributes, ==, WEBKIT_EDITOR_TYPING_ATTRIBUTE_NONE);
+
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD);
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH));
+
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD));
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC);
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH));
+
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC));
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE);
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH));
+
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE));
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH);
+
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD);
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC);
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH));
+
+ // Selections.
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveToBeginningOfDocument");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert_cmpuint(typingAttributes, ==, WEBKIT_EDITOR_TYPING_ATTRIBUTE_NONE);
+
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD);
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH));
+
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD));
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC);
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH));
+
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC));
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE);
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH));
+
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE));
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH);
+
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveForward");
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD);
+ g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC);
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE));
+ g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH));
+
+ webkit_web_view_execute_editing_command(test->m_webView, "SelectAll");
+ typingAttributes = test->waitUntilTypingAttributesChanged();
+ g_assert_cmpuint(typingAttributes, ==, WEBKIT_EDITOR_TYPING_ATTRIBUTE_NONE);
+}
+
+static void testWebViewEditorInsertImage(EditorTest* test, gconstpointer)
+{
+ test->loadHtml("<html><body></body></html>", "file:///");
+ test->waitUntilLoadFinished();
+ test->setEditable(true);
+
+ GUniquePtr<char> imagePath(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr));
+ GUniquePtr<char> imageURI(g_filename_to_uri(imagePath.get(), nullptr, nullptr));
+ webkit_web_view_execute_editing_command_with_argument(test->m_webView, WEBKIT_EDITING_COMMAND_INSERT_IMAGE, imageURI.get());
+ GUniqueOutPtr<GError> error;
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('IMG')[0].src", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error);
+ GUniquePtr<char> resultString(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(resultString.get(), ==, imageURI.get());
+}
+
+static void testWebViewEditorCreateLink(EditorTest* test, gconstpointer)
+{
+ test->loadHtml("<html><body onload=\"document.getSelection().selectAllChildren(document.body);\">webkitgtk.org</body></html>", nullptr);
+ test->waitUntilLoadFinished();
+ test->setEditable(true);
+
+ static const char* webkitGTKURL = "http://www.webkitgtk.org/";
+ webkit_web_view_execute_editing_command_with_argument(test->m_webView, WEBKIT_EDITING_COMMAND_CREATE_LINK, webkitGTKURL);
+ GUniqueOutPtr<GError> error;
+ WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('A')[0].href;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error);
+ GUniquePtr<char> resultString(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(resultString.get(), ==, webkitGTKURL);
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('A')[0].innerText;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error);
+ resultString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(resultString.get(), ==, "webkitgtk.org");
+
+ // When there isn't text selected, the URL is used as link text.
+ webkit_web_view_execute_editing_command(test->m_webView, "MoveToEndOfLine");
+ webkit_web_view_execute_editing_command_with_argument(test->m_webView, WEBKIT_EDITING_COMMAND_CREATE_LINK, webkitGTKURL);
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('A')[1].href;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error);
+ resultString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(resultString.get(), ==, webkitGTKURL);
+ javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('A')[1].innerText;", &error.outPtr());
+ g_assert(javascriptResult);
+ g_assert(!error);
+ resultString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
+ g_assert_cmpstr(resultString.get(), ==, webkitGTKURL);
+}
+
+void beforeAll()
+{
+ EditorTest::add("WebKitWebView", "editable/editable", testWebViewEditorEditable);
+ EditorTest::add("WebKitWebView", "cut-copy-paste/non-editable", testWebViewEditorCutCopyPasteNonEditable);
+ EditorTest::add("WebKitWebView", "cut-copy-paste/editable", testWebViewEditorCutCopyPasteEditable);
+ EditorTest::add("WebKitWebView", "select-all/non-editable", testWebViewEditorSelectAllNonEditable);
+ EditorTest::add("WebKitWebView", "select-all/editable", testWebViewEditorSelectAllEditable);
+ EditorTest::add("WebKitWebView", "editor-state/typing-attributes", testWebViewEditorEditorStateTypingAttributes);
+ EditorTest::add("WebKitWebView", "insert/image", testWebViewEditorInsertImage);
+ EditorTest::add("WebKitWebView", "insert/link", testWebViewEditorCreateLink);
+}
+
+void afterAll()
+{
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp
new file mode 100644
index 000000000..38c7d9412
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <JavaScriptCore/JSContextRef.h>
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <gio/gio.h>
+#include <gst/gst.h>
+#include <stdlib.h>
+#include <string.h>
+#include <webkit2/webkit-web-extension.h>
+#include <wtf/Deque.h>
+#include <wtf/ProcessID.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/CString.h>
+
+#define WEBKIT_DOM_USE_UNSTABLE_API
+#include <webkitdom/WebKitDOMWebKitNamespace.h>
+#include <webkitdom/WebKitDOMUserMessageHandlersNamespace.h>
+#include <webkitdom/WebKitDOMUserMessageHandler.h>
+
+static const char introspectionXML[] =
+ "<node>"
+ " <interface name='org.webkit.gtk.WebExtensionTest'>"
+ " <method name='GetTitle'>"
+ " <arg type='t' name='pageID' direction='in'/>"
+ " <arg type='s' name='title' direction='out'/>"
+ " </method>"
+ " <method name='AbortProcess'>"
+ " </method>"
+ " <method name='RunJavaScriptInIsolatedWorld'>"
+ " <arg type='t' name='pageID' direction='in'/>"
+ " <arg type='s' name='script' direction='in'/>"
+ " </method>"
+ " <method name='GetProcessIdentifier'>"
+ " <arg type='u' name='identifier' direction='out'/>"
+ " </method>"
+ " <method name='RemoveAVPluginsFromGSTRegistry'>"
+ " </method>"
+ " <signal name='DocumentLoaded'/>"
+ " <signal name='URIChanged'>"
+ " <arg type='s' name='uri' direction='out'/>"
+ " </signal>"
+ " </interface>"
+ "</node>";
+
+
+typedef enum {
+ DocumentLoadedSignal,
+ URIChangedSignal,
+} DelayedSignalType;
+
+struct DelayedSignal {
+ DelayedSignal(DelayedSignalType type)
+ : type(type)
+ {
+ }
+
+ DelayedSignal(DelayedSignalType type, const char* uri)
+ : type(type)
+ , uri(uri)
+ {
+ }
+
+ DelayedSignalType type;
+ CString uri;
+};
+
+Deque<DelayedSignal> delayedSignalsQueue;
+
+static void emitDocumentLoaded(GDBusConnection* connection)
+{
+ bool ok = g_dbus_connection_emit_signal(
+ connection,
+ 0,
+ "/org/webkit/gtk/WebExtensionTest",
+ "org.webkit.gtk.WebExtensionTest",
+ "DocumentLoaded",
+ 0,
+ 0);
+ g_assert(ok);
+}
+
+static void documentLoadedCallback(WebKitWebPage* webPage, WebKitWebExtension* extension)
+{
+ // FIXME: Too much code just to send a message, we need convenient custom API for this.
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(webPage);
+ GRefPtr<WebKitDOMDOMWindow> window = adoptGRef(webkit_dom_document_get_default_view(document));
+ if (WebKitDOMWebKitNamespace* webkit = webkit_dom_dom_window_get_webkit_namespace(window.get())) {
+ WebKitDOMUserMessageHandlersNamespace* messageHandlers = webkit_dom_webkit_namespace_get_message_handlers(webkit);
+ if (WebKitDOMUserMessageHandler* handler = webkit_dom_user_message_handlers_namespace_get_handler(messageHandlers, "dom"))
+ webkit_dom_user_message_handler_post_message(handler, "DocumentLoaded", nullptr);
+ }
+
+ webkit_dom_dom_window_webkit_message_handlers_post_message(window.get(), "dom-convenience", "DocumentLoaded");
+
+ gpointer data = g_object_get_data(G_OBJECT(extension), "dbus-connection");
+ if (data)
+ emitDocumentLoaded(G_DBUS_CONNECTION(data));
+ else
+ delayedSignalsQueue.append(DelayedSignal(DocumentLoadedSignal));
+}
+
+static void emitURIChanged(GDBusConnection* connection, const char* uri)
+{
+ bool ok = g_dbus_connection_emit_signal(
+ connection,
+ 0,
+ "/org/webkit/gtk/WebExtensionTest",
+ "org.webkit.gtk.WebExtensionTest",
+ "URIChanged",
+ g_variant_new("(s)", uri),
+ 0);
+ g_assert(ok);
+}
+
+static void uriChangedCallback(WebKitWebPage* webPage, GParamSpec* pspec, WebKitWebExtension* extension)
+{
+ gpointer data = g_object_get_data(G_OBJECT(extension), "dbus-connection");
+ if (data)
+ emitURIChanged(G_DBUS_CONNECTION(data), webkit_web_page_get_uri(webPage));
+ else
+ delayedSignalsQueue.append(DelayedSignal(URIChangedSignal, webkit_web_page_get_uri(webPage)));
+}
+
+static gboolean sendRequestCallback(WebKitWebPage*, WebKitURIRequest* request, WebKitURIResponse* redirectResponse, gpointer)
+{
+ const char* requestURI = webkit_uri_request_get_uri(request);
+ g_assert(requestURI);
+
+ if (const char* suffix = g_strrstr(requestURI, "/remove-this/javascript.js")) {
+ GUniquePtr<char> prefix(g_strndup(requestURI, strlen(requestURI) - strlen(suffix)));
+ GUniquePtr<char> newURI(g_strdup_printf("%s/javascript.js", prefix.get()));
+ webkit_uri_request_set_uri(request, newURI.get());
+ } else if (const char* suffix = g_strrstr(requestURI, "/remove-this/javascript-after-redirection.js")) {
+ // Redirected from /redirected.js, redirectResponse should be nullptr.
+ g_assert(WEBKIT_IS_URI_RESPONSE(redirectResponse));
+ g_assert(g_str_has_suffix(webkit_uri_response_get_uri(redirectResponse), "/redirected.js"));
+
+ GUniquePtr<char> prefix(g_strndup(requestURI, strlen(requestURI) - strlen(suffix)));
+ GUniquePtr<char> newURI(g_strdup_printf("%s/javascript-after-redirection.js", prefix.get()));
+ webkit_uri_request_set_uri(request, newURI.get());
+ } else if (g_str_has_suffix(requestURI, "/redirected.js")) {
+ // Original request, redirectResponse should be nullptr.
+ g_assert(!redirectResponse);
+ } else if (g_str_has_suffix(requestURI, "/add-do-not-track-header")) {
+ SoupMessageHeaders* headers = webkit_uri_request_get_http_headers(request);
+ g_assert(headers);
+ soup_message_headers_append(headers, "DNT", "1");
+ } else if (g_str_has_suffix(requestURI, "/cancel-this.js"))
+ return TRUE;
+
+ return FALSE;
+}
+
+static GVariant* serializeContextMenu(WebKitContextMenu* menu)
+{
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+ GList* items = webkit_context_menu_get_items(menu);
+ for (GList* it = items; it; it = g_list_next(it))
+ g_variant_builder_add(&builder, "u", webkit_context_menu_item_get_stock_action(WEBKIT_CONTEXT_MENU_ITEM(it->data)));
+ return g_variant_builder_end(&builder);
+}
+
+static GVariant* serializeNode(WebKitDOMNode* node)
+{
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+ g_variant_builder_add(&builder, "{sv}", "Name", g_variant_new_take_string(webkit_dom_node_get_node_name(node)));
+ g_variant_builder_add(&builder, "{sv}", "Type", g_variant_new_uint32(webkit_dom_node_get_node_type(node)));
+ g_variant_builder_add(&builder, "{sv}", "Contents", g_variant_new_take_string(webkit_dom_node_get_text_content(node)));
+ WebKitDOMNode* parent = webkit_dom_node_get_parent_node(node);
+ g_variant_builder_add(&builder, "{sv}", "Parent", parent ? g_variant_new_take_string(webkit_dom_node_get_node_name(parent)) : g_variant_new_string("ROOT"));
+ return g_variant_builder_end(&builder);
+}
+
+static gboolean contextMenuCallback(WebKitWebPage* page, WebKitContextMenu* menu, WebKitWebHitTestResult* hitTestResult, gpointer)
+{
+ const char* pageURI = webkit_web_page_get_uri(page);
+ if (!g_strcmp0(pageURI, "ContextMenuTestDefault")) {
+ webkit_context_menu_set_user_data(menu, serializeContextMenu(menu));
+ return FALSE;
+ }
+
+ if (!g_strcmp0(pageURI, "ContextMenuTestCustom")) {
+ // Remove Back and Forward, and add Inspector action.
+ webkit_context_menu_remove(menu, webkit_context_menu_first(menu));
+ webkit_context_menu_remove(menu, webkit_context_menu_first(menu));
+ webkit_context_menu_append(menu, webkit_context_menu_item_new_separator());
+ webkit_context_menu_append(menu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_INSPECT_ELEMENT));
+ webkit_context_menu_set_user_data(menu, serializeContextMenu(menu));
+ return TRUE;
+ }
+
+ if (!g_strcmp0(pageURI, "ContextMenuTestClear")) {
+ webkit_context_menu_remove_all(menu);
+ return TRUE;
+ }
+
+ if (!g_strcmp0(pageURI, "ContextMenuTestNode")) {
+ WebKitDOMNode* node = webkit_web_hit_test_result_get_node(hitTestResult);
+ g_assert(WEBKIT_DOM_IS_NODE(node));
+ webkit_context_menu_set_user_data(menu, serializeNode(node));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void pageCreatedCallback(WebKitWebExtension* extension, WebKitWebPage* webPage, gpointer)
+{
+ g_signal_connect(webPage, "document-loaded", G_CALLBACK(documentLoadedCallback), extension);
+ g_signal_connect(webPage, "notify::uri", G_CALLBACK(uriChangedCallback), extension);
+ g_signal_connect(webPage, "send-request", G_CALLBACK(sendRequestCallback), nullptr);
+ g_signal_connect(webPage, "context-menu", G_CALLBACK(contextMenuCallback), nullptr);
+}
+
+static JSValueRef echoCallback(JSContextRef jsContext, JSObjectRef, JSObjectRef, size_t argumentCount, const JSValueRef arguments[], JSValueRef*)
+{
+ if (argumentCount <= 0)
+ return JSValueMakeUndefined(jsContext);
+
+ JSRetainPtr<JSStringRef> string(Adopt, JSValueToStringCopy(jsContext, arguments[0], 0));
+ return JSValueMakeString(jsContext, string.get());
+}
+
+static void windowObjectCleared(WebKitScriptWorld* world, WebKitWebPage* page, WebKitFrame* frame, gpointer)
+{
+ JSGlobalContextRef jsContext = webkit_frame_get_javascript_context_for_script_world(frame, world);
+ g_assert(jsContext);
+ JSObjectRef globalObject = JSContextGetGlobalObject(jsContext);
+ g_assert(globalObject);
+
+ JSRetainPtr<JSStringRef> functionName(Adopt, JSStringCreateWithUTF8CString("echo"));
+ JSObjectRef function = JSObjectMakeFunctionWithCallback(jsContext, functionName.get(), echoCallback);
+ JSObjectSetProperty(jsContext, globalObject, functionName.get(), function, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, 0);
+}
+
+static WebKitWebPage* getWebPage(WebKitWebExtension* extension, uint64_t pageID, GDBusMethodInvocation* invocation)
+{
+ WebKitWebPage* page = webkit_web_extension_get_page(extension, pageID);
+ if (!page) {
+ g_dbus_method_invocation_return_error(
+ invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid page ID: %" G_GUINT64_FORMAT, pageID);
+ return 0;
+ }
+
+ g_assert_cmpuint(webkit_web_page_get_id(page), ==, pageID);
+ return page;
+}
+
+static void methodCallCallback(GDBusConnection* connection, const char* sender, const char* objectPath, const char* interfaceName, const char* methodName, GVariant* parameters, GDBusMethodInvocation* invocation, gpointer userData)
+{
+ if (g_strcmp0(interfaceName, "org.webkit.gtk.WebExtensionTest"))
+ return;
+
+ if (!g_strcmp0(methodName, "GetTitle")) {
+ uint64_t pageID;
+ g_variant_get(parameters, "(t)", &pageID);
+ WebKitWebPage* page = getWebPage(WEBKIT_WEB_EXTENSION(userData), pageID, invocation);
+ if (!page)
+ return;
+
+ WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
+ GUniquePtr<char> title(webkit_dom_document_get_title(document));
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", title.get()));
+ } else if (!g_strcmp0(methodName, "RunJavaScriptInIsolatedWorld")) {
+ uint64_t pageID;
+ const char* script;
+ g_variant_get(parameters, "(t&s)", &pageID, &script);
+ WebKitWebPage* page = getWebPage(WEBKIT_WEB_EXTENSION(userData), pageID, invocation);
+ if (!page)
+ return;
+
+ GRefPtr<WebKitScriptWorld> world = adoptGRef(webkit_script_world_new());
+ g_assert(webkit_script_world_get_default() != world.get());
+ WebKitFrame* frame = webkit_web_page_get_main_frame(page);
+ JSGlobalContextRef jsContext = webkit_frame_get_javascript_context_for_script_world(frame, world.get());
+ JSRetainPtr<JSStringRef> jsScript(Adopt, JSStringCreateWithUTF8CString(script));
+ JSEvaluateScript(jsContext, jsScript.get(), 0, 0, 0, 0);
+ g_dbus_method_invocation_return_value(invocation, 0);
+ } else if (!g_strcmp0(methodName, "AbortProcess")) {
+ abort();
+ } else if (!g_strcmp0(methodName, "GetProcessIdentifier")) {
+ g_dbus_method_invocation_return_value(invocation,
+ g_variant_new("(u)", static_cast<guint32>(getCurrentProcessID())));
+ } else if (!g_strcmp0(methodName, "RemoveAVPluginsFromGSTRegistry")) {
+ gst_init(nullptr, nullptr);
+ static const char* avPlugins[] = { "libav", "omx", "vaapi", nullptr };
+ GstRegistry* registry = gst_registry_get();
+ for (unsigned i = 0; avPlugins[i]; ++i) {
+ if (GstPlugin* plugin = gst_registry_find_plugin(registry, avPlugins[i])) {
+ gst_registry_remove_plugin(registry, plugin);
+ gst_object_unref(plugin);
+ }
+ }
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ }
+}
+
+static const GDBusInterfaceVTable interfaceVirtualTable = {
+ methodCallCallback, 0, 0, { 0, }
+};
+
+static void busAcquiredCallback(GDBusConnection* connection, const char* name, gpointer userData)
+{
+ static GDBusNodeInfo* introspectionData = 0;
+ if (!introspectionData)
+ introspectionData = g_dbus_node_info_new_for_xml(introspectionXML, 0);
+
+ GUniqueOutPtr<GError> error;
+ unsigned registrationID = g_dbus_connection_register_object(
+ connection,
+ "/org/webkit/gtk/WebExtensionTest",
+ introspectionData->interfaces[0],
+ &interfaceVirtualTable,
+ g_object_ref(userData),
+ static_cast<GDestroyNotify>(g_object_unref),
+ &error.outPtr());
+ if (!registrationID)
+ g_warning("Failed to register object: %s\n", error->message);
+
+ g_object_set_data(G_OBJECT(userData), "dbus-connection", connection);
+ while (delayedSignalsQueue.size()) {
+ DelayedSignal delayedSignal = delayedSignalsQueue.takeFirst();
+ switch (delayedSignal.type) {
+ case DocumentLoadedSignal:
+ emitDocumentLoaded(connection);
+ break;
+ case URIChangedSignal:
+ emitURIChanged(connection, delayedSignal.uri.data());
+ break;
+ }
+ }
+}
+
+extern "C" void webkit_web_extension_initialize_with_user_data(WebKitWebExtension* extension, GVariant* userData)
+{
+ g_signal_connect(extension, "page-created", G_CALLBACK(pageCreatedCallback), extension);
+ g_signal_connect(webkit_script_world_get_default(), "window-object-cleared", G_CALLBACK(windowObjectCleared), 0);
+
+ g_assert(userData);
+ g_assert(g_variant_is_of_type(userData, G_VARIANT_TYPE_UINT32));
+ GUniquePtr<char> busName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", g_variant_get_uint32(userData)));
+ g_bus_own_name(
+ G_BUS_TYPE_SESSION,
+ busName.get(),
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ busAcquiredCallback,
+ 0, 0,
+ g_object_ref(extension),
+ static_cast<GDestroyNotify>(g_object_unref));
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.cpp
new file mode 100644
index 000000000..ccd6f179a
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebProcessTest.h"
+
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <gio/gio.h>
+#include <wtf/HashSet.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/glib/GUniquePtr.h>
+
+static HashSet<GObject*> s_watchedObjects;
+
+typedef HashMap<String, std::function<std::unique_ptr<WebProcessTest> ()>> TestsMap;
+static TestsMap& testsMap()
+{
+ static NeverDestroyed<TestsMap> s_testsMap;
+ return s_testsMap;
+}
+
+void WebProcessTest::add(const String& testName, std::function<std::unique_ptr<WebProcessTest> ()> closure)
+{
+ testsMap().add(testName, WTF::move(closure));
+}
+
+void WebProcessTest::assertObjectIsDeletedWhenTestFinishes(GObject* object)
+{
+ s_watchedObjects.add(object);
+ g_object_weak_ref(object, [](gpointer, GObject* finalizedObject) {
+ s_watchedObjects.remove(finalizedObject);
+ }, nullptr);
+}
+
+std::unique_ptr<WebProcessTest> WebProcessTest::create(const String& testName)
+{
+ g_assert(testsMap().contains(testName));
+ return testsMap().get(testName)();
+}
+
+static JSValueRef runTest(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> stringValue(Adopt, JSValueToStringCopy(context, arguments[0], nullptr));
+ g_assert(stringValue);
+ size_t testPathLength = JSStringGetMaximumUTF8CStringSize(stringValue.get());
+ GUniquePtr<char> testPath(static_cast<char*>(g_malloc(testPathLength)));
+ JSStringGetUTF8CString(stringValue.get(), testPath.get(), testPathLength);
+
+ WebKitWebPage* webPage = WEBKIT_WEB_PAGE(JSObjectGetPrivate(thisObject));
+ g_assert(WEBKIT_IS_WEB_PAGE(webPage));
+ // Test /WebKitDOMNode/dom-cache is an exception, because it's called 3 times, so
+ // the WebPage is destroyed after the third time.
+ if (g_str_equal(testPath.get(), "WebKitDOMNode/dom-cache")) {
+ static unsigned domCacheTestRunCount = 0;
+ if (++domCacheTestRunCount == 3)
+ WebProcessTest::assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webPage));
+ } else
+ WebProcessTest::assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webPage));
+
+ std::unique_ptr<WebProcessTest> test = WebProcessTest::create(String::fromUTF8(testPath.get()));
+ return JSValueMakeBoolean(context, test->runTest(g_strrstr(testPath.get(), "/") + 1, webPage));
+}
+
+static const JSStaticFunction webProcessTestRunnerStaticFunctions[] =
+{
+ { "runTest", runTest, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { nullptr, nullptr, 0 }
+};
+
+static void webProcessTestRunnerFinalize(JSObjectRef object)
+{
+ g_object_unref(JSObjectGetPrivate(object));
+
+ if (s_watchedObjects.isEmpty())
+ return;
+
+ g_print("Leaked objects in WebProcess:");
+ for (const auto object : s_watchedObjects)
+ g_print(" %s(%p)", g_type_name_from_instance(reinterpret_cast<GTypeInstance*>(object)), object);
+ g_print("\n");
+
+ g_assert(s_watchedObjects.isEmpty());
+}
+
+static void windowObjectClearedCallback(WebKitScriptWorld* world, WebKitWebPage* webPage, WebKitFrame* frame, WebKitWebExtension* extension)
+{
+ JSGlobalContextRef context = webkit_frame_get_javascript_context_for_script_world(frame, world);
+ JSObjectRef globalObject = JSContextGetGlobalObject(context);
+
+ JSClassDefinition classDefinition = kJSClassDefinitionEmpty;
+ classDefinition.className = "WebProcessTestRunner";
+ classDefinition.staticFunctions = webProcessTestRunnerStaticFunctions;
+ classDefinition.finalize = webProcessTestRunnerFinalize;
+
+ JSClassRef jsClass = JSClassCreate(&classDefinition);
+ JSObjectRef classObject = JSObjectMake(context, jsClass, g_object_ref(webPage));
+ JSRetainPtr<JSStringRef> propertyString(Adopt, JSStringCreateWithUTF8CString("WebProcessTestRunner"));
+ JSObjectSetProperty(context, globalObject, propertyString.get(), classObject, kJSPropertyAttributeNone, nullptr);
+ JSClassRelease(jsClass);
+}
+
+extern "C" void webkit_web_extension_initialize(WebKitWebExtension* extension)
+{
+ g_signal_connect(webkit_script_world_get_default(), "window-object-cleared", G_CALLBACK(windowObjectClearedCallback), extension);
+}
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.h b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.h
new file mode 100644
index 000000000..3715eb91e
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <webkit2/webkit-web-extension.h>
+#include <wtf/HashMap.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+class WebProcessTest {
+public:
+ virtual ~WebProcessTest() { }
+ virtual bool runTest(const char* testName, WebKitWebPage*) = 0;
+
+ static void assertObjectIsDeletedWhenTestFinishes(GObject*);
+
+ static void add(const String& testName, std::function<std::unique_ptr<WebProcessTest> ()>);
+ static std::unique_ptr<WebProcessTest> create(const String& testName);
+};
+
+#define REGISTER_TEST(ClassName, TestName) \
+ WebProcessTest::add(String::fromUTF8(TestName), ClassName::create)
+
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/blank.ico b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/blank.ico
new file mode 100644
index 000000000..ea848b991
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/blank.ico
Binary files differ
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/link-title.js b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/link-title.js
new file mode 100644
index 000000000..2c824da38
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/link-title.js
@@ -0,0 +1 @@
+window.document.getElementById('WebKitLink').title;
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-cert.pem b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-cert.pem
new file mode 100644
index 000000000..b34301f25
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-cert.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB9jCCAV+gAwIBAgIJALeuXBo+vwz9MA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
+BAMMCTEyNy4wLjAuMTAeFw0xMjA3MTIxMjQ4MjRaFw0yMjA3MTAxMjQ4MjRaMBQx
+EjAQBgNVBAMMCTEyNy4wLjAuMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
+0TUzOQxHBIKDD2mkuq+tU92mQvDZg73B0G+Nhr2T2G6MbcLqIwjg1QYtBZWJ83tZ
+xMMEfiweHLF85Z9ohavAgxJlKG7YmvZO79KkFpmjV2W5CVRm0eYMPnzmxNCoaYqo
+DLl0zsH6KZOLPKu/fX4eDX9XpAP1f83hWB1UFBmHKN8CAwEAAaNQME4wHQYDVR0O
+BBYEFDHv5ZQ1BdmhzTsDUEoY55EXyUdKMB8GA1UdIwQYMBaAFDHv5ZQ1BdmhzTsD
+UEoY55EXyUdKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAh3qMBx7v
+jSodMf3OyTqTLE7deLnmnCeBVpgzxRZEoizcGqYcjiqO27i5N5Z6KVQsnITnLiyC
+mUtuR5KnF69uTKUw4m/ugZe5whjig5Mq2l410KVK6EeG4tdLlfXR+wi4U5K4KjP6
+p4nchQUXLa2zcbJn+VBexJn6/9wdhr+DUGY=
+-----END CERTIFICATE-----
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-key.pem b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-key.pem
new file mode 100644
index 000000000..9036222ce
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-key.pem
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANE1MzkMRwSCgw9p
+pLqvrVPdpkLw2YO9wdBvjYa9k9hujG3C6iMI4NUGLQWVifN7WcTDBH4sHhyxfOWf
+aIWrwIMSZShu2Jr2Tu/SpBaZo1dluQlUZtHmDD585sTQqGmKqAy5dM7B+imTizyr
+v31+Hg1/V6QD9X/N4VgdVBQZhyjfAgMBAAECgYB2QwOUsRsIMprRwJ9tJNfvO7G7
+z5i1/zOrlxPC4jHMPBnIBlICwgcOhLI4oOLdr5H8R12n0VqoT7DRwP396iwlJipF
+iO1heDMn/8z8LPGwkCK/+ck04rMDksxWIdMwYKBXt9ahnJ/xRLzQ1/3AJiAGnoe5
+/QLXQweofd4mmfsjKQJBAO2CwT7uMP6nMjXgtVMJq5QP8UbeCS1sEOPJJbHuDxJB
+/HePQHBjq4kzG6CL4oO7T+5fDv4g+fIIHzuXerZ0imsCQQDhfmiTIc9OucEIfg6/
+ms0JiKSmWc+qoiOCtrILuQvFoNwJRciQANqeJs6wpaDvevSUvBLGfG/7b3HvaE5X
+iqBdAkBEQIvp2qcHtuJN60oQF7pPrRknxUyb2e8sljQX4pJAK+gyL19ULMAxiBdL
+Vod8VYqNtJFpY+6Pp9fZ1xjzb6ALAkEA4JzrDAw0lQXA+3WduUw4ixOadr2ldyG0
+36KebcDwsfZO18m0Q4UmPz0Gy7zgN0wxzuochaw0W6+iPUiYKOlEXQJBAMWQrPlu
+rrinoZS2f8doJ9BNNUa+RNpMug6UXc55qoUJlyiXEh+tu4AaMOtxuGIyC0sAcuw6
+XdAPVPXKd7Mne70=
+-----END PRIVATE KEY-----
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/webkit2gtk-tests.gresource.xml b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/webkit2gtk-tests.gresource.xml
new file mode 100644
index 000000000..b38868420
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/webkit2gtk-tests.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/webkit/webkit2gtk/tests/">
+ <file alias="link-title.js">Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/link-title.js</file>
+ </gresource>
+</gresources>
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2ObjC/CustomProtocolsInvalidScheme_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2ObjC/CustomProtocolsInvalidScheme_Bundle.cpp
new file mode 100644
index 000000000..287f32ff5
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2ObjC/CustomProtocolsInvalidScheme_Bundle.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include <WebKit/WKBundlePage.h>
+
+namespace TestWebKitAPI {
+
+static WKBundlePagePolicyAction decidePolicyForNavigationAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef request, WKTypeRef*, const void*)
+{
+ if (WKBundlePageCanHandleRequest(request))
+ return WKBundlePagePolicyActionUse;
+ return WKBundlePagePolicyActionPassThrough;
+}
+
+class CustomProtocolInvalidSchemeTest : public InjectedBundleTest {
+public:
+ CustomProtocolInvalidSchemeTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+private:
+ virtual void didCreatePage(WKBundleRef, WKBundlePageRef bundlePage) override
+ {
+ WKBundlePagePolicyClientV0 policyClient;
+ memset(&policyClient, 0, sizeof(policyClient));
+
+ policyClient.base.version = 0;
+ policyClient.decidePolicyForNavigationAction = decidePolicyForNavigationAction;
+
+ WKBundlePageSetPolicyClient(bundlePage, &policyClient.base);
+ }
+};
+
+static InjectedBundleTest::Register<CustomProtocolInvalidSchemeTest> registrar("CustomProtocolInvalidSchemeTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2ObjC/PreventImageLoadWithAutoResizing_Bundle.cpp b/Tools/TestWebKitAPI/Tests/WebKit2ObjC/PreventImageLoadWithAutoResizing_Bundle.cpp
new file mode 100644
index 000000000..dde94407a
--- /dev/null
+++ b/Tools/TestWebKitAPI/Tests/WebKit2ObjC/PreventImageLoadWithAutoResizing_Bundle.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_HAVE_C_SPI
+
+#include "InjectedBundleTest.h"
+#include "PlatformUtilities.h"
+#include "Test.h"
+
+#include <WebKit/WKBundlePage.h>
+
+#include <wtf/Assertions.h>
+
+namespace TestWebKitAPI {
+
+class DenyWillSendRequestTest : public InjectedBundleTest {
+public:
+ DenyWillSendRequestTest(const std::string& identifier)
+ : InjectedBundleTest(identifier)
+ {
+ }
+
+ static WKURLRequestRef willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void *clientInfo)
+ {
+ return 0;
+ }
+
+ virtual void didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
+ {
+ WKBundlePageResourceLoadClientV0 resourceLoadClient;
+ memset(&resourceLoadClient, 0, sizeof(resourceLoadClient));
+
+ resourceLoadClient.base.version = 0;
+ resourceLoadClient.willSendRequestForFrame = willSendRequestForFrame;
+
+ WKBundlePageSetResourceLoadClient(page, &resourceLoadClient.base);
+
+ }
+};
+
+static InjectedBundleTest::Register<DenyWillSendRequestTest> registrar("DenyWillSendRequestTest");
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/TestsController.cpp b/Tools/TestWebKitAPI/TestsController.cpp
new file mode 100644
index 000000000..913305d7b
--- /dev/null
+++ b/Tools/TestWebKitAPI/TestsController.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestsController.h"
+
+#include <wtf/Threading.h>
+
+namespace TestWebKitAPI {
+
+class Printer : public ::testing::EmptyTestEventListener {
+ virtual void OnTestPartResult(const ::testing::TestPartResult& test_part_result)
+ {
+ if (!test_part_result.failed())
+ return;
+
+ std::stringstream stream;
+ stream << "\n" << test_part_result.file_name() << ":" << test_part_result.line_number() << "\n" << test_part_result.summary() << "\n\n";
+ failures += stream.str();
+ }
+
+ virtual void OnTestEnd(const ::testing::TestInfo& test_info)
+ {
+ if (test_info.result()->Passed())
+ std::cout << "**PASS** " << test_info.test_case_name() << "." << test_info.name() << "\n";
+ else
+ std::cout << "**FAIL** " << test_info.test_case_name() << "." << test_info.name() << "\n" << failures;
+
+ failures = std::string();
+ }
+
+ std::string failures;
+};
+
+TestsController& TestsController::singleton()
+{
+ static NeverDestroyed<TestsController> shared;
+ return shared;
+}
+
+TestsController::TestsController()
+{
+ // FIXME: We currently initialize threading here to avoid assertion failures from
+ // the ThreadRestrictionVerifier - https://bugs.webkit.org/show_bug.cgi?id=66112
+ // We should make sure that all objects tested either initialize threading or inherit from
+ // ThreadSafeRefCounted so that we don't have to initialize threading at all here.
+ WTF::initializeThreading();
+}
+
+bool TestsController::run(int argc, char** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ ::testing::TestEventListeners& listeners = ::testing::UnitTest::GetInstance()->listeners();
+ delete listeners.Release(listeners.default_result_printer());
+ listeners.Append(new Printer);
+
+ return !RUN_ALL_TESTS();
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/TestsController.h b/Tools/TestWebKitAPI/TestsController.h
new file mode 100644
index 000000000..ea8496853
--- /dev/null
+++ b/Tools/TestWebKitAPI/TestsController.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TestsController_h
+#define TestsController_h
+
+#include <wtf/NeverDestroyed.h>
+
+namespace TestWebKitAPI {
+
+class TestsController {
+public:
+ static TestsController& singleton();
+
+ bool run(int argc, char** argv);
+
+private:
+ TestsController();
+ ~TestsController();
+
+ friend class WTF::NeverDestroyed<TestsController>;
+};
+
+} // namespace TestWebKitAPI
+
+#endif // TestsController_h
diff --git a/Tools/TestWebKitAPI/WTFStringUtilities.h b/Tools/TestWebKitAPI/WTFStringUtilities.h
new file mode 100644
index 000000000..29a876788
--- /dev/null
+++ b/Tools/TestWebKitAPI/WTFStringUtilities.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WTFStringUtilities_h
+#define WTFStringUtilities_h
+
+#include <wtf/Assertions.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/WTFString.h>
+
+namespace WTF {
+
+inline std::ostream& operator<<(std::ostream& os, const String& string)
+{
+ return os << string.utf8().data();
+}
+
+}
+
+#endif // WTFStringUtilities_h
diff --git a/Tools/TestWebKitAPI/config.h b/Tools/TestWebKitAPI/config.h
new file mode 100644
index 000000000..17c9f4fa2
--- /dev/null
+++ b/Tools/TestWebKitAPI/config.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H && defined(BUILDING_WITH_CMAKE)
+#include "cmakeconfig.h"
+#endif
+
+#include <WebCore/PlatformExportMacros.h>
+#include <runtime/JSExportMacros.h>
+
+#if defined(__APPLE__) && __APPLE__
+
+#ifdef __OBJC__
+#if PLATFORM(IOS)
+#import <Foundation/Foundation.h>
+#else
+#import <Cocoa/Cocoa.h>
+#endif
+#endif
+
+#elif PLATFORM(WIN)
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#if PLATFORM(WIN_CAIRO)
+#undef USE_CG
+#define USE_CAIRO 1
+#define USE_CURL 1
+#ifndef _WINSOCKAPI_
+#define _WINSOCKAPI_ // Prevent inclusion of winsock.h in windows.h
+#endif
+#else
+#define USE_CG 1
+#undef USE_CAIRO
+#undef USE_CURL
+#endif
+
+#endif // PLATFORM(WIN)
+
+#include <stdint.h>
+
+#if !PLATFORM(IOS) && !PLATFORM(WIN) && !(PLATFORM(GTK) && !defined(BUILDING_WEBKIT2__))
+#include <WebKit/WebKit2_C.h>
+#endif
+
+#ifdef __clang__
+// Work around the less strict coding standards of the gtest framework.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-variable"
+#endif
+
+#ifdef __cplusplus
+#include <gtest/gtest.h>
+#endif
+
+#ifdef __clang__
+// Finish working around the less strict coding standards of the gtest framework.
+#pragma clang diagnostic pop
+#endif
+
+#if PLATFORM(COCOA) && defined(__OBJC__)
+#import <WebKit/WebKit.h>
+#endif
+
+#if !PLATFORM(IOS)
+#define WK_HAVE_C_SPI 1
+#endif
diff --git a/Tools/TestWebKitAPI/gtk/InjectedBundleControllerGtk.cpp b/Tools/TestWebKitAPI/gtk/InjectedBundleControllerGtk.cpp
new file mode 100644
index 000000000..5d55002cc
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/InjectedBundleControllerGtk.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InjectedBundleController.h"
+
+namespace TestWebKitAPI {
+
+void InjectedBundleController::platformInitialize()
+{
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/gtk/PlatformUtilitiesGtk.cpp b/Tools/TestWebKitAPI/gtk/PlatformUtilitiesGtk.cpp
new file mode 100644
index 000000000..f7b231fcc
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/PlatformUtilitiesGtk.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PlatformUtilities.h"
+
+#include <gtk/gtk.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+
+namespace TestWebKitAPI {
+namespace Util {
+
+static gboolean checkTestFinished(gpointer userData)
+{
+ bool* done = static_cast<bool*>(userData);
+
+ if (*done)
+ gtk_main_quit();
+
+ return !*done;
+}
+
+void run(bool* done)
+{
+ g_idle_add(checkTestFinished, done);
+ gtk_main();
+}
+
+void sleep(double seconds)
+{
+ g_usleep(seconds * 1000000);
+}
+
+static char* getFilenameFromEnvironmentVariableAsUTF8(const char* variableName)
+{
+ const char* value = g_getenv(variableName);
+ if (!value) {
+ g_printerr("%s environment variable not found\n", variableName);
+ exit(1);
+ }
+ gsize bytesWritten;
+ return g_filename_to_utf8(value, -1, 0, &bytesWritten, 0);
+}
+
+WKStringRef createInjectedBundlePath()
+{
+ GUniquePtr<char> injectedBundlePath(getFilenameFromEnvironmentVariableAsUTF8("TEST_WEBKIT_API_WEBKIT2_INJECTED_BUNDLE_PATH"));
+ GUniquePtr<char> injectedBundleFilename(g_build_filename(injectedBundlePath.get(), "libTestWebKitAPIInjectedBundle.so", nullptr));
+ return WKStringCreateWithUTF8CString(injectedBundleFilename.get());
+}
+
+WKURLRef createURLForResource(const char* resource, const char* extension)
+{
+ GUniquePtr<char> testResourcesPath(getFilenameFromEnvironmentVariableAsUTF8("TEST_WEBKIT_API_WEBKIT2_RESOURCES_PATH"));
+ GUniquePtr<char> resourceBasename(g_strdup_printf("%s.%s", resource, extension));
+ GUniquePtr<char> resourceFilename(g_build_filename(testResourcesPath.get(), resourceBasename.get(), nullptr));
+ GRefPtr<GFile> resourceFile = adoptGRef(g_file_new_for_path(resourceFilename.get()));
+ GUniquePtr<char> resourceURI(g_file_get_uri(resourceFile.get()));
+ return WKURLCreateWithUTF8CString(resourceURI.get());
+}
+
+WKURLRef URLForNonExistentResource()
+{
+ return WKURLCreateWithUTF8CString("file:///does-not-exist.html");
+}
+
+bool isKeyDown(WKNativeEventPtr event)
+{
+ return event->type == GDK_KEY_PRESS;
+}
+
+} // namespace Util
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/gtk/PlatformWebViewGtk.cpp b/Tools/TestWebKitAPI/gtk/PlatformWebViewGtk.cpp
new file mode 100644
index 000000000..5f0eb4040
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/PlatformWebViewGtk.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PlatformWebView.h"
+
+#include <WebCore/GUniquePtrGtk.h>
+#include <gtk/gtk.h>
+#include <wtf/glib/GUniquePtr.h>
+
+namespace TestWebKitAPI {
+
+PlatformWebView::PlatformWebView(WKContextRef contextRef, WKPageGroupRef pageGroupRef)
+{
+ m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ m_view = WKViewCreate(contextRef, pageGroupRef, nullptr);
+ gtk_container_add(GTK_CONTAINER(m_window), GTK_WIDGET(m_view));
+ gtk_widget_show(GTK_WIDGET(m_view));
+ gtk_widget_show(m_window);
+}
+
+PlatformWebView::~PlatformWebView()
+{
+ gtk_widget_destroy(m_window);
+}
+
+WKPageRef PlatformWebView::page() const
+{
+ return WKViewGetPage(m_view);
+}
+
+void PlatformWebView::resizeTo(unsigned width, unsigned height)
+{
+ gtk_window_resize(GTK_WINDOW(m_window), width, height);
+}
+
+static void doKeyStroke(GtkWidget* viewWidget, unsigned int keyVal)
+{
+ GUniquePtr<GdkEvent> event(gdk_event_new(GDK_KEY_PRESS));
+ event->key.keyval = keyVal;
+ event->key.time = GDK_CURRENT_TIME;
+ event->key.state = 0;
+ event->key.window = gtk_widget_get_window(viewWidget);
+ g_object_ref(event->key.window);
+ gdk_event_set_device(event.get(), gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget))));
+
+ // When synthesizing an event, an invalid hardware_keycode value can cause it to be badly processed by GTK+.
+ GUniqueOutPtr<GdkKeymapKey> keys;
+ int keysCount;
+ if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), keyVal, &keys.outPtr(), &keysCount))
+ event->key.hardware_keycode = keys.get()[0].keycode;
+
+ gtk_main_do_event(event.get());
+ event->key.type = GDK_KEY_RELEASE;
+ gtk_main_do_event(event.get());
+}
+
+void PlatformWebView::simulateSpacebarKeyPress()
+{
+ GtkWidget* viewWidget = GTK_WIDGET(m_view);
+ if (!gtk_widget_get_realized(viewWidget))
+ gtk_widget_show(m_window);
+ doKeyStroke(viewWidget, GDK_KEY_KP_Space);
+}
+
+void PlatformWebView::simulateAltKeyPress()
+{
+ GtkWidget* viewWidget = GTK_WIDGET(m_view);
+ if (!gtk_widget_get_realized(viewWidget))
+ gtk_widget_show(m_window);
+ doKeyStroke(viewWidget, GDK_KEY_Alt_L);
+}
+
+static void doMouseButtonEvent(GtkWidget* viewWidget, GdkEventType eventType, int x, int y, unsigned int button)
+{
+ GUniquePtr<GdkEvent> event(gdk_event_new(eventType));
+ event->button.x = x;
+ event->button.y = y;
+ event->button.button = button;
+ event->button.time = GDK_CURRENT_TIME;
+ event->button.axes = 0;
+ event->button.state = 0;
+ event->button.window = gtk_widget_get_window(viewWidget);
+ g_object_ref(event->button.window);
+ event->button.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget)));
+
+ int xRoot, yRoot;
+ gdk_window_get_root_coords(gtk_widget_get_window(viewWidget), x, y, &xRoot, &yRoot);
+ event->button.x_root = xRoot;
+ event->button.y_root = yRoot;
+ gtk_main_do_event(event.get());
+}
+
+void PlatformWebView::simulateRightClick(unsigned x, unsigned y)
+{
+ GtkWidget* viewWidget = GTK_WIDGET(m_view);
+ if (!gtk_widget_get_realized(viewWidget))
+ gtk_widget_show(m_window);
+ doMouseButtonEvent(viewWidget, GDK_BUTTON_PRESS, x, y, 3);
+ doMouseButtonEvent(viewWidget, GDK_BUTTON_RELEASE, x, y, 3);
+}
+
+void PlatformWebView::simulateMouseMove(unsigned x, unsigned y)
+{
+ GUniquePtr<GdkEvent> event(gdk_event_new(GDK_MOTION_NOTIFY));
+ event->motion.x = x;
+ event->motion.y = y;
+ event->motion.time = GDK_CURRENT_TIME;
+ event->motion.state = 0;
+ event->motion.axes = 0;
+
+ GtkWidget* viewWidget = GTK_WIDGET(m_view);
+ if (!gtk_widget_get_realized(viewWidget))
+ gtk_widget_show(m_window);
+ event->motion.window = gtk_widget_get_window(viewWidget);
+ g_object_ref(event->motion.window);
+ event->motion.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget)));
+
+ int xRoot, yRoot;
+ gdk_window_get_root_coords(gtk_widget_get_window(viewWidget), x, y, &xRoot, &yRoot);
+ event->motion.x_root = xRoot;
+ event->motion.y_root = yRoot;
+ gtk_main_do_event(event.get());
+}
+
+} // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/gtk/WebKit2Gtk/LoadTrackingTest.cpp b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/LoadTrackingTest.cpp
new file mode 100644
index 000000000..a59cb9210
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/LoadTrackingTest.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "LoadTrackingTest.h"
+
+#include <webkit2/webkit2.h>
+
+static void loadChangedCallback(WebKitWebView* webView, WebKitLoadEvent loadEvent, LoadTrackingTest* test)
+{
+ switch (loadEvent) {
+ case WEBKIT_LOAD_STARTED:
+ g_assert(webkit_web_view_is_loading(webView));
+ g_assert_cmpstr(test->m_activeURI.data(), ==, webkit_web_view_get_uri(webView));
+ test->provisionalLoadStarted();
+ break;
+ case WEBKIT_LOAD_REDIRECTED:
+ g_assert(webkit_web_view_is_loading(webView));
+ test->m_activeURI = webkit_web_view_get_uri(webView);
+ if (!test->m_redirectURI.isNull())
+ g_assert_cmpstr(test->m_redirectURI.data(), ==, test->m_activeURI.data());
+ test->provisionalLoadReceivedServerRedirect();
+ break;
+ case WEBKIT_LOAD_COMMITTED: {
+ g_assert(webkit_web_view_is_loading(webView));
+ g_assert_cmpstr(test->m_activeURI.data(), ==, webkit_web_view_get_uri(webView));
+
+ // Check that on committed we always have a main resource with a response.
+ WebKitWebResource* resource = webkit_web_view_get_main_resource(webView);
+ g_assert(resource);
+ g_assert(webkit_web_resource_get_response(resource));
+
+ test->loadCommitted();
+ break;
+ }
+ case WEBKIT_LOAD_FINISHED:
+ if (!test->m_loadFailed) {
+ g_assert(!webkit_web_view_is_loading(webView));
+ g_assert_cmpstr(test->m_activeURI.data(), ==, webkit_web_view_get_uri(webView));
+ } else if (!g_error_matches(test->m_error.get(), WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_CANCELLED)) {
+ // When a new load is started before the previous one has finished, we receive the load-finished signal
+ // of the ongoing load while we already have a provisional URL for the new load. This is the only case
+ // where isloading is true when the load has finished.
+ g_assert(!webkit_web_view_is_loading(webView));
+ }
+ test->loadFinished();
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void loadFailedCallback(WebKitWebView* webView, WebKitLoadEvent loadEvent, const char* failingURI, GError* error, LoadTrackingTest* test)
+{
+ test->m_loadFailed = true;
+
+ g_assert(error);
+ test->m_error.reset(g_error_copy(error));
+
+ if (!g_error_matches(error, WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_CANCELLED)) {
+ // When a new load is started before the previous one has finished, we receive the load-failed signal
+ // of the ongoing load while we already have a provisional URL for the new load. This is the only case
+ // where is-loading is true when the load has failed.
+ g_assert(!webkit_web_view_is_loading(webView));
+ }
+
+ switch (loadEvent) {
+ case WEBKIT_LOAD_STARTED:
+ g_assert_cmpstr(test->m_activeURI.data(), ==, failingURI);
+ test->provisionalLoadFailed(failingURI, error);
+ break;
+ case WEBKIT_LOAD_COMMITTED:
+ g_assert_cmpstr(test->m_activeURI.data(), ==, webkit_web_view_get_uri(webView));
+ test->loadFailed(failingURI, error);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static gboolean loadFailedWithTLSErrorsCallback(WebKitWebView* webView, const char* failingURI, GTlsCertificate* certificate, GTlsCertificateFlags tlsErrors, LoadTrackingTest* test)
+{
+ test->m_loadFailed = true;
+ g_assert(!webkit_web_view_is_loading(webView));
+ g_assert_cmpstr(test->m_activeURI.data(), ==, failingURI);
+ g_assert(G_IS_TLS_CERTIFICATE(certificate));
+ g_assert(tlsErrors);
+ return test->loadFailedWithTLSErrors(failingURI, certificate, tlsErrors);
+}
+
+static void estimatedProgressChangedCallback(GObject*, GParamSpec*, LoadTrackingTest* test)
+{
+ test->estimatedProgressChanged();
+}
+
+LoadTrackingTest::LoadTrackingTest()
+ : m_runLoadUntilCompletion(false)
+ , m_loadFailed(false)
+{
+ g_signal_connect(m_webView, "load-changed", G_CALLBACK(loadChangedCallback), this);
+ g_signal_connect(m_webView, "load-failed", G_CALLBACK(loadFailedCallback), this);
+ g_signal_connect(m_webView, "load-failed-with-tls-errors", G_CALLBACK(loadFailedWithTLSErrorsCallback), this);
+ g_signal_connect(m_webView, "notify::estimated-load-progress", G_CALLBACK(estimatedProgressChangedCallback), this);
+
+ g_assert(!webkit_web_view_get_uri(m_webView));
+}
+
+LoadTrackingTest::~LoadTrackingTest()
+{
+ g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+}
+
+void LoadTrackingTest::waitUntilLoadFinished()
+{
+ m_estimatedProgress = 0;
+ m_runLoadUntilCompletion = true;
+ g_main_loop_run(m_mainLoop);
+}
+
+void LoadTrackingTest::provisionalLoadStarted()
+{
+ m_loadEvents.append(ProvisionalLoadStarted);
+}
+
+void LoadTrackingTest::provisionalLoadReceivedServerRedirect()
+{
+ m_loadEvents.append(ProvisionalLoadReceivedServerRedirect);
+}
+
+void LoadTrackingTest::provisionalLoadFailed(const gchar* failingURI, GError* error)
+{
+ m_loadEvents.append(ProvisionalLoadFailed);
+}
+
+void LoadTrackingTest::loadCommitted()
+{
+ m_loadEvents.append(LoadCommitted);
+}
+
+void LoadTrackingTest::loadFinished()
+{
+ m_loadEvents.append(LoadFinished);
+ if (m_runLoadUntilCompletion)
+ g_main_loop_quit(m_mainLoop);
+}
+
+void LoadTrackingTest::loadFailed(const gchar* failingURI, GError* error)
+{
+ m_loadEvents.append(LoadFailed);
+}
+
+bool LoadTrackingTest::loadFailedWithTLSErrors(const gchar* /*failingURI*/, GTlsCertificate*, GTlsCertificateFlags)
+{
+ m_loadEvents.append(LoadFailedWithTLSErrors);
+ return false;
+}
+
+void LoadTrackingTest::estimatedProgressChanged()
+{
+ double progress = webkit_web_view_get_estimated_load_progress(m_webView);
+ g_assert_cmpfloat(m_estimatedProgress, <, progress);
+ m_estimatedProgress = progress;
+}
+
+void LoadTrackingTest::loadURI(const char* uri)
+{
+ m_loadEvents.clear();
+ m_estimatedProgress = 0;
+ m_error.reset();
+ WebViewTest::loadURI(uri);
+}
+
+void LoadTrackingTest::loadHtml(const char* html, const char* baseURI)
+{
+ m_loadEvents.clear();
+ m_estimatedProgress = 0;
+ m_error.reset();
+ WebViewTest::loadHtml(html, baseURI);
+}
+
+void LoadTrackingTest::loadPlainText(const char* plainText)
+{
+ m_loadEvents.clear();
+ m_estimatedProgress = 0;
+ m_error.reset();
+ WebViewTest::loadPlainText(plainText);
+}
+
+void LoadTrackingTest::loadBytes(GBytes* bytes, const char* mimeType, const char* encoding, const char* baseURI)
+{
+ m_loadEvents.clear();
+ m_estimatedProgress = 0;
+ m_error.reset();
+ WebViewTest::loadBytes(bytes, mimeType, encoding, baseURI);
+}
+
+void LoadTrackingTest::loadRequest(WebKitURIRequest* request)
+{
+ m_loadEvents.clear();
+ m_estimatedProgress = 0;
+ m_error.reset();
+ WebViewTest::loadRequest(request);
+}
+
+void LoadTrackingTest::reload()
+{
+ m_loadEvents.clear();
+ m_estimatedProgress = 0;
+ m_error.reset();
+ webkit_web_view_reload(m_webView);
+}
+
+void LoadTrackingTest::goBack()
+{
+ m_loadEvents.clear();
+ m_estimatedProgress = 0;
+ m_error.reset();
+ WebViewTest::goBack();
+}
+
+void LoadTrackingTest::goForward()
+{
+ m_loadEvents.clear();
+ m_estimatedProgress = 0;
+ m_error.reset();
+ WebViewTest::goForward();
+}
diff --git a/Tools/TestWebKitAPI/gtk/WebKit2Gtk/LoadTrackingTest.h b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/LoadTrackingTest.h
new file mode 100644
index 000000000..c92184734
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/LoadTrackingTest.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef LoadTrackingTest_h
+#define LoadTrackingTest_h
+
+#include "TestMain.h"
+#include "WebViewTest.h"
+#include <wtf/Vector.h>
+
+class LoadTrackingTest : public WebViewTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(LoadTrackingTest);
+ LoadTrackingTest();
+ virtual ~LoadTrackingTest();
+ void waitUntilLoadFinished();
+
+ virtual void provisionalLoadStarted();
+ virtual void provisionalLoadReceivedServerRedirect();
+ virtual void provisionalLoadFailed(const gchar* failingURI, GError*);
+ virtual bool loadFailedWithTLSErrors(const gchar* failingURI, GTlsCertificate*, GTlsCertificateFlags);
+ virtual void loadCommitted();
+ virtual void loadFinished();
+ virtual void loadFailed(const char* failingURI, GError*);
+ virtual void estimatedProgressChanged();
+
+ void loadURI(const char* uri);
+ void loadHtml(const char* html, const char* baseURI);
+ void loadPlainText(const char* plainText);
+ void loadRequest(WebKitURIRequest*);
+ void loadBytes(GBytes*, const char* mimeType, const char* encoding, const char* baseURI);
+ void reload();
+ void goBack();
+ void goForward();
+
+ void setRedirectURI(const char* uri) { m_redirectURI = uri; }
+
+ enum LoadEvents {
+ ProvisionalLoadStarted,
+ ProvisionalLoadReceivedServerRedirect,
+ ProvisionalLoadFailed,
+ LoadCommitted,
+ LoadFinished,
+ LoadFailed,
+ LoadFailedWithTLSErrors
+ };
+ bool m_runLoadUntilCompletion;
+ bool m_loadFailed;
+ GUniquePtr<GError> m_error;
+ Vector<LoadEvents> m_loadEvents;
+ float m_estimatedProgress;
+ CString m_redirectURI;
+};
+
+#endif // LoadTrackingTest_h
diff --git a/Tools/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.cpp b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.cpp
new file mode 100644
index 000000000..80809400d
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "TestMain.h"
+
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+uint32_t Test::s_webExtensionID = 0;
+
+void beforeAll();
+void afterAll();
+
+static GUniquePtr<char> testDataDirectory(g_dir_make_tmp("WebKit2GtkTests-XXXXXX", nullptr));
+
+const char* Test::dataDirectory()
+{
+ return testDataDirectory.get();
+}
+
+static void registerGResource(void)
+{
+ GUniquePtr<char> resourcesPath(g_build_filename(WEBKIT_EXEC_PATH, "TestWebKitAPI", "WebKit2Gtk", "resources", "webkit2gtk-tests-resources.gresource", nullptr));
+ GResource* resource = g_resource_load(resourcesPath.get(), 0);
+ g_assert(resource);
+
+ g_resources_register(resource);
+ g_resource_unref(resource);
+}
+
+static void removeNonEmptyDirectory(const char* directoryPath)
+{
+ GDir* directory = g_dir_open(directoryPath, 0, 0);
+ g_assert(directory);
+ const char* fileName;
+ while ((fileName = g_dir_read_name(directory))) {
+ GUniquePtr<char> filePath(g_build_filename(directoryPath, fileName, nullptr));
+ if (g_file_test(filePath.get(), G_FILE_TEST_IS_DIR))
+ removeNonEmptyDirectory(filePath.get());
+ else
+ g_unlink(filePath.get());
+ }
+ g_dir_close(directory);
+ g_rmdir(directoryPath);
+}
+
+int main(int argc, char** argv)
+{
+ g_unsetenv("DBUS_SESSION_BUS_ADDRESS");
+ gtk_test_init(&argc, &argv, 0);
+ g_setenv("WEBKIT_EXEC_PATH", WEBKIT_EXEC_PATH, FALSE);
+ g_setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, FALSE);
+ g_setenv("LC_ALL", "C", TRUE);
+ g_setenv("GIO_USE_VFS", "local", TRUE);
+ g_setenv("GSETTINGS_BACKEND", "memory", TRUE);
+ // Get rid of runtime warnings about deprecated properties and signals, since they break the tests.
+ g_setenv("G_ENABLE_DIAGNOSTIC", "0", TRUE);
+ g_test_bug_base("https://bugs.webkit.org/");
+
+ registerGResource();
+
+ beforeAll();
+ int returnValue = g_test_run();
+ afterAll();
+
+ removeNonEmptyDirectory(testDataDirectory.get());
+
+ return returnValue;
+}
+
diff --git a/Tools/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.h b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.h
new file mode 100644
index 000000000..6054fe09d
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef TestMain_h
+#define TestMain_h
+
+#include <cairo.h>
+#include <glib-object.h>
+#include <webkit2/webkit2.h>
+#include <wtf/HashSet.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/CString.h>
+
+#define MAKE_GLIB_TEST_FIXTURE(ClassName) \
+ static void setUp(ClassName* fixture, gconstpointer data) \
+ { \
+ new (fixture) ClassName; \
+ } \
+ static void tearDown(ClassName* fixture, gconstpointer data) \
+ { \
+ fixture->~ClassName(); \
+ } \
+ static void add(const char* suiteName, const char* testName, void (*testFunc)(ClassName*, const void*)) \
+ { \
+ GUniquePtr<gchar> testPath(g_strdup_printf("/webkit2/%s/%s", suiteName, testName)); \
+ g_test_add(testPath.get(), ClassName, 0, ClassName::setUp, testFunc, ClassName::tearDown); \
+ }
+
+#define ASSERT_CMP_CSTRING(s1, cmp, s2) \
+ do { \
+ CString __s1 = (s1); \
+ CString __s2 = (s2); \
+ if (g_strcmp0(__s1.data(), __s2.data()) cmp 0) ; \
+ else \
+ g_assertion_message_cmpstr(G_LOG_DOMAIN, __FILE__, __LINE__, \
+ G_STRFUNC, #s1 " " #cmp " " #s2, __s1.data(), #cmp, __s2.data()); \
+ } while (0)
+
+
+class Test {
+public:
+ MAKE_GLIB_TEST_FIXTURE(Test);
+
+ static const char* dataDirectory();
+
+ static void initializeWebExtensionsCallback(WebKitWebContext* context, Test* test)
+ {
+ test->initializeWebExtensions();
+ }
+
+ Test()
+ {
+ GUniquePtr<char> localStorageDirectory(g_build_filename(dataDirectory(), "local-storage", nullptr));
+ GUniquePtr<char> indexedDBDirectory(g_build_filename(dataDirectory(), "indexeddb", nullptr));
+ GUniquePtr<char> diskCacheDirectory(g_build_filename(dataDirectory(), "disk-cache", nullptr));
+ GUniquePtr<char> applicationCacheDirectory(g_build_filename(dataDirectory(), "appcache", nullptr));
+ GUniquePtr<char> webSQLDirectory(g_build_filename(dataDirectory(), "websql", nullptr));
+ GRefPtr<WebKitWebsiteDataManager> websiteDataManager = adoptGRef(webkit_website_data_manager_new(
+ "local-storage-directory", localStorageDirectory.get(), "indexeddb-directory", indexedDBDirectory.get(),
+ "disk-cache-directory", diskCacheDirectory.get(), "offline-application-cache-directory", applicationCacheDirectory.get(),
+ "websql-directory", webSQLDirectory.get(), nullptr));
+
+ m_webContext = adoptGRef(webkit_web_context_new_with_website_data_manager(websiteDataManager.get()));
+ g_signal_connect(m_webContext.get(), "initialize-web-extensions", G_CALLBACK(initializeWebExtensionsCallback), this);
+ }
+
+ ~Test()
+ {
+ g_signal_handlers_disconnect_matched(m_webContext.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+ m_webContext = nullptr;
+ if (m_watchedObjects.isEmpty())
+ return;
+
+ g_print("Leaked objects:");
+ HashSet<GObject*>::const_iterator end = m_watchedObjects.end();
+ for (HashSet<GObject*>::const_iterator it = m_watchedObjects.begin(); it != end; ++it)
+ g_print(" %s(%p)", g_type_name_from_instance(reinterpret_cast<GTypeInstance*>(*it)), *it);
+ g_print("\n");
+
+ g_assert(m_watchedObjects.isEmpty());
+ }
+
+ virtual void initializeWebExtensions()
+ {
+ webkit_web_context_set_web_extensions_directory(m_webContext.get(), WEBKIT_TEST_WEB_EXTENSIONS_DIR);
+ webkit_web_context_set_web_extensions_initialization_user_data(m_webContext.get(), g_variant_new_uint32(++s_webExtensionID));
+ }
+
+ static void objectFinalized(Test* test, GObject* finalizedObject)
+ {
+ test->m_watchedObjects.remove(finalizedObject);
+ }
+
+ void assertObjectIsDeletedWhenTestFinishes(GObject* object)
+ {
+ m_watchedObjects.add(object);
+ g_object_weak_ref(object, reinterpret_cast<GWeakNotify>(objectFinalized), this);
+ }
+
+
+ enum ResourcesDir {
+ WebKit2GTKResources,
+ WebKit2Resources,
+ };
+
+ static CString getResourcesDir(ResourcesDir resourcesDir = WebKit2GTKResources)
+ {
+ switch (resourcesDir) {
+ case WebKit2GTKResources: {
+ GUniquePtr<char> resourcesDir(g_build_filename(WEBKIT_SRC_DIR, "Tools", "TestWebKitAPI", "Tests", "WebKit2Gtk", "resources", nullptr));
+ return resourcesDir.get();
+ }
+ case WebKit2Resources: {
+ GUniquePtr<char> resourcesDir(g_build_filename(WEBKIT_SRC_DIR, "Tools", "TestWebKitAPI", "Tests", "WebKit2", nullptr));
+ return resourcesDir.get();
+ }
+ }
+ }
+
+ void addLogFatalFlag(unsigned flag)
+ {
+ unsigned fatalMask = g_log_set_always_fatal(static_cast<GLogLevelFlags>(G_LOG_FATAL_MASK));
+ fatalMask |= flag;
+ g_log_set_always_fatal(static_cast<GLogLevelFlags>(fatalMask));
+ }
+
+ void removeLogFatalFlag(unsigned flag)
+ {
+ unsigned fatalMask = g_log_set_always_fatal(static_cast<GLogLevelFlags>(G_LOG_FATAL_MASK));
+ fatalMask &= ~flag;
+ g_log_set_always_fatal(static_cast<GLogLevelFlags>(fatalMask));
+ }
+
+ static bool cairoSurfacesEqual(cairo_surface_t* s1, cairo_surface_t* s2)
+ {
+ return (cairo_image_surface_get_format(s1) == cairo_image_surface_get_format(s2)
+ && cairo_image_surface_get_width(s1) == cairo_image_surface_get_width(s2)
+ && cairo_image_surface_get_height(s1) == cairo_image_surface_get_height(s2)
+ && cairo_image_surface_get_stride(s1) == cairo_image_surface_get_stride(s2)
+ && !memcmp(const_cast<const void*>(reinterpret_cast<void*>(cairo_image_surface_get_data(s1))),
+ const_cast<const void*>(reinterpret_cast<void*>(cairo_image_surface_get_data(s2))),
+ cairo_image_surface_get_height(s1)*cairo_image_surface_get_stride(s1)));
+ }
+
+ HashSet<GObject*> m_watchedObjects;
+ GRefPtr<WebKitWebContext> m_webContext;
+ static uint32_t s_webExtensionID;
+};
+
+#endif // TestMain_h
diff --git a/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestBus.cpp b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestBus.cpp
new file mode 100644
index 000000000..1cf241461
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestBus.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebKitTestBus.h"
+
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/WTFString.h>
+
+WebKitTestBus::WebKitTestBus()
+ : m_pid(-1)
+{
+}
+
+bool WebKitTestBus::run()
+{
+ // FIXME: Use GTestDBus when we bump glib to 2.34.
+ GUniquePtr<char> dbusLaunch(g_find_program_in_path("dbus-launch"));
+ if (!dbusLaunch) {
+ g_warning("Error starting DBUS daemon: dbus-launch not found in path");
+ return false;
+ }
+
+ GUniqueOutPtr<char> output;
+ GUniqueOutPtr<GError> error;
+ if (!g_spawn_command_line_sync(dbusLaunch.get(), &output.outPtr(), 0, 0, &error.outPtr())) {
+ g_warning("Error starting DBUS daemon: %s", error->message);
+ return false;
+ }
+
+ String outputString = String::fromUTF8(output.get());
+ Vector<String> lines;
+ outputString.split(UChar('\n'), /* allowEmptyEntries */ false, lines);
+ for (size_t i = 0; i < lines.size(); ++i) {
+ char** keyValue = g_strsplit(lines[i].utf8().data(), "=", 2);
+ g_assert_cmpuint(g_strv_length(keyValue), ==, 2);
+ if (!g_strcmp0(keyValue[0], "DBUS_SESSION_BUS_ADDRESS")) {
+ m_address = keyValue[1];
+ g_setenv("DBUS_SESSION_BUS_ADDRESS", keyValue[1], TRUE);
+ } else if (!g_strcmp0(keyValue[0], "DBUS_SESSION_BUS_PID"))
+ m_pid = g_ascii_strtoll(keyValue[1], 0, 10);
+ g_strfreev(keyValue);
+ }
+
+ return m_pid > 0;
+}
+
+WebKitTestBus::~WebKitTestBus()
+{
+ g_unsetenv("DBUS_SESSION_BUS_ADDRESS");
+
+ if (m_pid != -1)
+ kill(m_pid, SIGTERM);
+}
+
+GDBusConnection* WebKitTestBus::getOrCreateConnection()
+{
+ if (m_connection)
+ return m_connection.get();
+
+ g_assert(!m_address.isNull());
+ m_connection = adoptGRef(g_dbus_connection_new_for_address_sync(m_address.data(),
+ static_cast<GDBusConnectionFlags>(G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION),
+ 0, 0, 0));
+ return m_connection.get();
+}
+
+static void onNameAppeared(GDBusConnection*, const char*, const char*, gpointer userData)
+{
+ g_main_loop_quit(static_cast<GMainLoop*>(userData));
+}
+
+GDBusProxy* WebKitTestBus::createProxy(const char* serviceName, const char* objectPath, const char* interfaceName, GMainLoop* mainLoop)
+{
+ unsigned watcherID = g_bus_watch_name_on_connection(getOrCreateConnection(), serviceName, G_BUS_NAME_WATCHER_FLAGS_NONE, onNameAppeared, 0, mainLoop, 0);
+ g_main_loop_run(mainLoop);
+ g_bus_unwatch_name(watcherID);
+
+ GDBusProxy* proxy = g_dbus_proxy_new_sync(
+ connection(),
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ 0, // GDBusInterfaceInfo
+ serviceName,
+ objectPath,
+ interfaceName,
+ 0, // GCancellable
+ 0);
+ g_assert(proxy);
+ return proxy;
+}
diff --git a/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestBus.h b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestBus.h
new file mode 100644
index 000000000..03bcc3ae5
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestBus.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WebKitTestBus_h
+#define WebKitTestBus_h
+
+#include <gio/gio.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/text/CString.h>
+
+class WebKitTestBus {
+public:
+ WebKitTestBus();
+ virtual ~WebKitTestBus();
+
+ bool run();
+ GDBusProxy* createProxy(const char* serviceName, const char* objectPath, const char* interfaceName, GMainLoop*);
+ GDBusConnection* connection() const { return m_connection.get(); }
+
+private:
+ GDBusConnection* getOrCreateConnection();
+
+ pid_t m_pid;
+ CString m_address;
+ GRefPtr<GDBusConnection> m_connection;
+};
+
+#endif // WebKitTestBus_h
diff --git a/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestServer.cpp b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestServer.cpp
new file mode 100644
index 000000000..5c75143ca
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestServer.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebKitTestServer.h"
+
+#include "TestMain.h"
+#include <wtf/glib/GUniquePtr.h>
+
+WebKitTestServer::WebKitTestServer(ServerType type)
+{
+ GUniquePtr<char> sslCertificateFile;
+ GUniquePtr<char> sslKeyFile;
+ if (type == ServerHTTPS) {
+ CString resourcesDir = Test::getResourcesDir();
+ sslCertificateFile.reset(g_build_filename(resourcesDir.data(), "test-cert.pem", NULL));
+ sslKeyFile.reset(g_build_filename(resourcesDir.data(), "test-key.pem", NULL));
+ }
+
+ GRefPtr<SoupAddress> address = adoptGRef(soup_address_new("127.0.0.1", SOUP_ADDRESS_ANY_PORT));
+ soup_address_resolve_sync(address.get(), 0);
+
+ m_soupServer = adoptGRef(soup_server_new(SOUP_SERVER_INTERFACE, address.get(),
+ SOUP_SERVER_SSL_CERT_FILE, sslCertificateFile.get(),
+ SOUP_SERVER_SSL_KEY_FILE, sslKeyFile.get(), nullptr));
+ m_baseURI = type == ServerHTTPS ? soup_uri_new("https://127.0.0.1/") : soup_uri_new("http://127.0.0.1/");
+ soup_uri_set_port(m_baseURI, soup_server_get_port(m_soupServer.get()));
+}
+
+WebKitTestServer::~WebKitTestServer()
+{
+ soup_uri_free(m_baseURI);
+}
+
+void WebKitTestServer::run(SoupServerCallback serverCallback)
+{
+ soup_server_run_async(m_soupServer.get());
+ soup_server_add_handler(m_soupServer.get(), 0, serverCallback, 0, 0);
+}
+
+CString WebKitTestServer::getURIForPath(const char* path)
+{
+ SoupURI* uri = soup_uri_new_with_base(m_baseURI, path);
+ GUniquePtr<gchar> uriString(soup_uri_to_string(uri, FALSE));
+ soup_uri_free(uri);
+ return uriString.get();
+}
+
diff --git a/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestServer.h b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestServer.h
new file mode 100644
index 000000000..235d6c93a
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestServer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WebKitTestServer_h
+#define WebKitTestServer_h
+
+#include <libsoup/soup.h>
+#include <webkit2/webkit2.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/text/CString.h>
+
+class WebKitTestServer {
+public:
+
+ enum ServerType {
+ ServerHTTP,
+ ServerHTTPS
+ };
+
+ WebKitTestServer(ServerType = ServerHTTP);
+ virtual ~WebKitTestServer();
+
+ SoupURI* baseURI() { return m_baseURI; }
+
+ CString getURIForPath(const char* path);
+ void run(SoupServerCallback);
+
+private:
+ GRefPtr<SoupServer> m_soupServer;
+ SoupURI* m_baseURI;
+};
+
+#endif // WebKitTestServer_h
diff --git a/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.cpp b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.cpp
new file mode 100644
index 000000000..14f2c1466
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.cpp
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ * Portions Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebViewTest.h"
+
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <WebCore/GUniquePtrGtk.h>
+#include <wtf/glib/GMainLoopSource.h>
+
+WebViewTest::WebViewTest(WebKitUserContentManager* userContentManager)
+ : m_webView(WEBKIT_WEB_VIEW(g_object_ref_sink(g_object_new(WEBKIT_TYPE_WEB_VIEW, "web-context", m_webContext.get(), "user-content-manager", userContentManager, nullptr))))
+ , m_mainLoop(g_main_loop_new(nullptr, TRUE))
+ , m_parentWindow(nullptr)
+ , m_javascriptResult(nullptr)
+ , m_resourceDataSize(0)
+ , m_surface(nullptr)
+ , m_expectedWebProcessCrash(false)
+{
+ assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_webView));
+ g_signal_connect(m_webView, "web-process-crashed", G_CALLBACK(WebViewTest::webProcessCrashed), this);
+}
+
+WebViewTest::~WebViewTest()
+{
+ if (m_parentWindow)
+ gtk_widget_destroy(m_parentWindow);
+ if (m_javascriptResult)
+ webkit_javascript_result_unref(m_javascriptResult);
+ if (m_surface)
+ cairo_surface_destroy(m_surface);
+ g_object_unref(m_webView);
+ g_main_loop_unref(m_mainLoop);
+}
+
+gboolean WebViewTest::webProcessCrashed(WebKitWebView*, WebViewTest* test)
+{
+ if (test->m_expectedWebProcessCrash) {
+ test->m_expectedWebProcessCrash = false;
+ return FALSE;
+ }
+ g_assert_not_reached();
+ return TRUE;
+}
+
+void WebViewTest::loadURI(const char* uri)
+{
+ m_activeURI = uri;
+ webkit_web_view_load_uri(m_webView, uri);
+ g_assert(webkit_web_view_is_loading(m_webView));
+ g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+}
+
+void WebViewTest::loadHtml(const char* html, const char* baseURI)
+{
+ if (!baseURI)
+ m_activeURI = "about:blank";
+ else
+ m_activeURI = baseURI;
+ webkit_web_view_load_html(m_webView, html, baseURI);
+ g_assert(webkit_web_view_is_loading(m_webView));
+ g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+}
+
+void WebViewTest::loadPlainText(const char* plainText)
+{
+ m_activeURI = "about:blank";
+ webkit_web_view_load_plain_text(m_webView, plainText);
+#if 0
+ // FIXME: Pending API request URL no set when loading plain text.
+ // See https://bugs.webkit.org/show_bug.cgi?id=136916.
+ g_assert(webkit_web_view_is_loading(m_webView));
+ g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+#endif
+}
+
+void WebViewTest::loadBytes(GBytes* bytes, const char* mimeType, const char* encoding, const char* baseURI)
+{
+ if (!baseURI)
+ m_activeURI = "about:blank";
+ else
+ m_activeURI = baseURI;
+ webkit_web_view_load_bytes(m_webView, bytes, mimeType, encoding, baseURI);
+#if 0
+ // FIXME: Pending API request URL no set when loading data.
+ // See https://bugs.webkit.org/show_bug.cgi?id=136916.
+ g_assert(webkit_web_view_is_loading(m_webView));
+ g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+#endif
+}
+
+void WebViewTest::loadRequest(WebKitURIRequest* request)
+{
+ m_activeURI = webkit_uri_request_get_uri(request);
+ webkit_web_view_load_request(m_webView, request);
+ g_assert(webkit_web_view_is_loading(m_webView));
+ g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+}
+
+void WebViewTest::loadAlternateHTML(const char* html, const char* contentURI, const char* baseURI)
+{
+ m_activeURI = contentURI;
+ webkit_web_view_load_alternate_html(m_webView, html, contentURI, baseURI);
+#if 0
+ // FIXME: Pending API request URL no set when loading Alternate HTML.
+ // See https://bugs.webkit.org/show_bug.cgi?id=136916.
+ g_assert(webkit_web_view_is_loading(m_webView));
+#endif
+ g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+}
+
+void WebViewTest::goBack()
+{
+ bool canGoBack = webkit_web_view_can_go_back(m_webView);
+ if (canGoBack) {
+ WebKitBackForwardList* list = webkit_web_view_get_back_forward_list(m_webView);
+ WebKitBackForwardListItem* item = webkit_back_forward_list_get_nth_item(list, -1);
+ m_activeURI = webkit_back_forward_list_item_get_original_uri(item);
+ }
+
+ // Call go_back even when can_go_back returns FALSE to check nothing happens.
+ webkit_web_view_go_back(m_webView);
+ if (canGoBack) {
+ g_assert(webkit_web_view_is_loading(m_webView));
+ g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+ }
+}
+
+void WebViewTest::goForward()
+{
+ bool canGoForward = webkit_web_view_can_go_forward(m_webView);
+ if (canGoForward) {
+ WebKitBackForwardList* list = webkit_web_view_get_back_forward_list(m_webView);
+ WebKitBackForwardListItem* item = webkit_back_forward_list_get_nth_item(list, 1);
+ m_activeURI = webkit_back_forward_list_item_get_original_uri(item);
+ }
+
+ // Call go_forward even when can_go_forward returns FALSE to check nothing happens.
+ webkit_web_view_go_forward(m_webView);
+ if (canGoForward) {
+ g_assert(webkit_web_view_is_loading(m_webView));
+ g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+ }
+}
+
+void WebViewTest::goToBackForwardListItem(WebKitBackForwardListItem* item)
+{
+ m_activeURI = webkit_back_forward_list_item_get_original_uri(item);
+ webkit_web_view_go_to_back_forward_list_item(m_webView, item);
+ g_assert(webkit_web_view_is_loading(m_webView));
+ g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+}
+
+void WebViewTest::quitMainLoop()
+{
+ g_main_loop_quit(m_mainLoop);
+}
+
+void WebViewTest::quitMainLoopAfterProcessingPendingEvents()
+{
+ while (gtk_events_pending())
+ gtk_main_iteration();
+ quitMainLoop();
+}
+
+void WebViewTest::wait(double seconds)
+{
+ GMainLoopSource::scheduleAfterDelayAndDeleteOnDestroy("WebViewTest wait", [this] { quitMainLoop(); },
+ std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::duration<double>(seconds)));
+ g_main_loop_run(m_mainLoop);
+}
+
+static void loadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test)
+{
+ if (loadEvent != WEBKIT_LOAD_FINISHED)
+ return;
+ g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(loadChanged), test);
+ g_main_loop_quit(test->m_mainLoop);
+}
+
+void WebViewTest::waitUntilLoadFinished()
+{
+ g_signal_connect(m_webView, "load-changed", G_CALLBACK(loadChanged), this);
+ g_main_loop_run(m_mainLoop);
+}
+
+static void titleChanged(WebKitWebView* webView, GParamSpec*, WebViewTest* test)
+{
+ if (!test->m_expectedTitle.isNull() && test->m_expectedTitle != webkit_web_view_get_title(webView))
+ return;
+
+ g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(titleChanged), test);
+ g_main_loop_quit(test->m_mainLoop);
+}
+
+void WebViewTest::waitUntilTitleChangedTo(const char* expectedTitle)
+{
+ m_expectedTitle = expectedTitle;
+ g_signal_connect(m_webView, "notify::title", G_CALLBACK(titleChanged), this);
+ g_main_loop_run(m_mainLoop);
+ m_expectedTitle = CString();
+}
+
+void WebViewTest::waitUntilTitleChanged()
+{
+ waitUntilTitleChangedTo(0);
+}
+
+static gboolean parentWindowMapped(GtkWidget* widget, GdkEvent*, WebViewTest* test)
+{
+ g_signal_handlers_disconnect_by_func(widget, reinterpret_cast<void*>(parentWindowMapped), test);
+ g_main_loop_quit(test->m_mainLoop);
+
+ return FALSE;
+}
+
+void WebViewTest::showInWindow(GtkWindowType windowType)
+{
+ g_assert(!m_parentWindow);
+ m_parentWindow = gtk_window_new(windowType);
+ gtk_container_add(GTK_CONTAINER(m_parentWindow), GTK_WIDGET(m_webView));
+ gtk_widget_show(GTK_WIDGET(m_webView));
+ gtk_widget_show(m_parentWindow);
+}
+
+void WebViewTest::showInWindowAndWaitUntilMapped(GtkWindowType windowType, int width, int height)
+{
+ g_assert(!m_parentWindow);
+ m_parentWindow = gtk_window_new(windowType);
+ if (width && height)
+ gtk_window_resize(GTK_WINDOW(m_parentWindow), width, height);
+ gtk_container_add(GTK_CONTAINER(m_parentWindow), GTK_WIDGET(m_webView));
+ gtk_widget_show(GTK_WIDGET(m_webView));
+
+ g_signal_connect(m_parentWindow, "map-event", G_CALLBACK(parentWindowMapped), this);
+ gtk_widget_show(m_parentWindow);
+ g_main_loop_run(m_mainLoop);
+}
+
+void WebViewTest::resizeView(int width, int height)
+{
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(GTK_WIDGET(m_webView), &allocation);
+ if (width != -1)
+ allocation.width = width;
+ if (height != -1)
+ allocation.height = height;
+ gtk_widget_size_allocate(GTK_WIDGET(m_webView), &allocation);
+}
+
+void WebViewTest::selectAll()
+{
+ webkit_web_view_execute_editing_command(m_webView, "SelectAll");
+}
+
+bool WebViewTest::isEditable()
+{
+ return webkit_web_view_is_editable(m_webView);
+}
+
+void WebViewTest::setEditable(bool editable)
+{
+ webkit_web_view_set_editable(m_webView, editable);
+}
+
+static void resourceGetDataCallback(GObject* object, GAsyncResult* result, gpointer userData)
+{
+ size_t dataSize;
+ GUniqueOutPtr<GError> error;
+ unsigned char* data = webkit_web_resource_get_data_finish(WEBKIT_WEB_RESOURCE(object), result, &dataSize, &error.outPtr());
+ g_assert(data);
+
+ WebViewTest* test = static_cast<WebViewTest*>(userData);
+ test->m_resourceData.reset(reinterpret_cast<char*>(data));
+ test->m_resourceDataSize = dataSize;
+ g_main_loop_quit(test->m_mainLoop);
+}
+
+const char* WebViewTest::mainResourceData(size_t& mainResourceDataSize)
+{
+ m_resourceDataSize = 0;
+ m_resourceData.reset();
+ WebKitWebResource* resource = webkit_web_view_get_main_resource(m_webView);
+ g_assert(resource);
+
+ webkit_web_resource_get_data(resource, 0, resourceGetDataCallback, this);
+ g_main_loop_run(m_mainLoop);
+
+ mainResourceDataSize = m_resourceDataSize;
+ return m_resourceData.get();
+}
+
+void WebViewTest::mouseMoveTo(int x, int y, unsigned mouseModifiers)
+{
+ g_assert(m_parentWindow);
+ GtkWidget* viewWidget = GTK_WIDGET(m_webView);
+ g_assert(gtk_widget_get_realized(viewWidget));
+
+ GUniquePtr<GdkEvent> event(gdk_event_new(GDK_MOTION_NOTIFY));
+ event->motion.x = x;
+ event->motion.y = y;
+
+ event->motion.time = GDK_CURRENT_TIME;
+ event->motion.window = gtk_widget_get_window(viewWidget);
+ g_object_ref(event->motion.window);
+ event->motion.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget)));
+ event->motion.state = mouseModifiers;
+ event->motion.axes = 0;
+
+ int xRoot, yRoot;
+ gdk_window_get_root_coords(gtk_widget_get_window(viewWidget), x, y, &xRoot, &yRoot);
+ event->motion.x_root = xRoot;
+ event->motion.y_root = yRoot;
+ gtk_main_do_event(event.get());
+}
+
+void WebViewTest::clickMouseButton(int x, int y, unsigned button, unsigned mouseModifiers)
+{
+ doMouseButtonEvent(GDK_BUTTON_PRESS, x, y, button, mouseModifiers);
+ doMouseButtonEvent(GDK_BUTTON_RELEASE, x, y, button, mouseModifiers);
+}
+
+void WebViewTest::keyStroke(unsigned keyVal, unsigned keyModifiers)
+{
+ g_assert(m_parentWindow);
+ GtkWidget* viewWidget = GTK_WIDGET(m_webView);
+ g_assert(gtk_widget_get_realized(viewWidget));
+
+ GUniquePtr<GdkEvent> event(gdk_event_new(GDK_KEY_PRESS));
+ event->key.keyval = keyVal;
+
+ event->key.time = GDK_CURRENT_TIME;
+ event->key.window = gtk_widget_get_window(viewWidget);
+ g_object_ref(event->key.window);
+ gdk_event_set_device(event.get(), gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget))));
+ event->key.state = keyModifiers;
+
+ // When synthesizing an event, an invalid hardware_keycode value can cause it to be badly processed by GTK+.
+ GUniqueOutPtr<GdkKeymapKey> keys;
+ int keysCount;
+ if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), keyVal, &keys.outPtr(), &keysCount))
+ event->key.hardware_keycode = keys.get()[0].keycode;
+
+ gtk_main_do_event(event.get());
+ event->key.type = GDK_KEY_RELEASE;
+ gtk_main_do_event(event.get());
+}
+
+void WebViewTest::doMouseButtonEvent(GdkEventType eventType, int x, int y, unsigned button, unsigned mouseModifiers)
+{
+ g_assert(m_parentWindow);
+ GtkWidget* viewWidget = GTK_WIDGET(m_webView);
+ g_assert(gtk_widget_get_realized(viewWidget));
+
+ GUniquePtr<GdkEvent> event(gdk_event_new(eventType));
+ event->button.window = gtk_widget_get_window(viewWidget);
+ g_object_ref(event->button.window);
+
+ event->button.time = GDK_CURRENT_TIME;
+ event->button.x = x;
+ event->button.y = y;
+ event->button.axes = 0;
+ event->button.state = mouseModifiers;
+ event->button.button = button;
+
+ event->button.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget)));
+
+ int xRoot, yRoot;
+ gdk_window_get_root_coords(gtk_widget_get_window(viewWidget), x, y, &xRoot, &yRoot);
+ event->button.x_root = xRoot;
+ event->button.y_root = yRoot;
+ gtk_main_do_event(event.get());
+}
+
+static void runJavaScriptReadyCallback(GObject*, GAsyncResult* result, WebViewTest* test)
+{
+ test->m_javascriptResult = webkit_web_view_run_javascript_finish(test->m_webView, result, test->m_javascriptError);
+ g_main_loop_quit(test->m_mainLoop);
+}
+
+static void runJavaScriptFromGResourceReadyCallback(GObject*, GAsyncResult* result, WebViewTest* test)
+{
+ test->m_javascriptResult = webkit_web_view_run_javascript_from_gresource_finish(test->m_webView, result, test->m_javascriptError);
+ g_main_loop_quit(test->m_mainLoop);
+}
+
+WebKitJavascriptResult* WebViewTest::runJavaScriptAndWaitUntilFinished(const char* javascript, GError** error)
+{
+ if (m_javascriptResult)
+ webkit_javascript_result_unref(m_javascriptResult);
+ m_javascriptResult = 0;
+ m_javascriptError = error;
+ webkit_web_view_run_javascript(m_webView, javascript, 0, reinterpret_cast<GAsyncReadyCallback>(runJavaScriptReadyCallback), this);
+ g_main_loop_run(m_mainLoop);
+
+ return m_javascriptResult;
+}
+
+WebKitJavascriptResult* WebViewTest::runJavaScriptFromGResourceAndWaitUntilFinished(const char* resource, GError** error)
+{
+ if (m_javascriptResult)
+ webkit_javascript_result_unref(m_javascriptResult);
+ m_javascriptResult = 0;
+ m_javascriptError = error;
+ webkit_web_view_run_javascript_from_gresource(m_webView, resource, 0, reinterpret_cast<GAsyncReadyCallback>(runJavaScriptFromGResourceReadyCallback), this);
+ g_main_loop_run(m_mainLoop);
+
+ return m_javascriptResult;
+}
+
+static char* jsValueToCString(JSGlobalContextRef context, JSValueRef value)
+{
+ g_assert(value);
+ g_assert(JSValueIsString(context, value));
+
+ JSRetainPtr<JSStringRef> stringValue(Adopt, JSValueToStringCopy(context, value, 0));
+ g_assert(stringValue);
+
+ size_t cStringLength = JSStringGetMaximumUTF8CStringSize(stringValue.get());
+ char* cString = static_cast<char*>(g_malloc(cStringLength));
+ JSStringGetUTF8CString(stringValue.get(), cString, cStringLength);
+ return cString;
+}
+
+char* WebViewTest::javascriptResultToCString(WebKitJavascriptResult* javascriptResult)
+{
+ JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
+ g_assert(context);
+ return jsValueToCString(context, webkit_javascript_result_get_value(javascriptResult));
+}
+
+double WebViewTest::javascriptResultToNumber(WebKitJavascriptResult* javascriptResult)
+{
+ JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
+ g_assert(context);
+ JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
+ g_assert(value);
+ g_assert(JSValueIsNumber(context, value));
+
+ return JSValueToNumber(context, value, 0);
+}
+
+bool WebViewTest::javascriptResultToBoolean(WebKitJavascriptResult* javascriptResult)
+{
+ JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
+ g_assert(context);
+ JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
+ g_assert(value);
+ g_assert(JSValueIsBoolean(context, value));
+
+ return JSValueToBoolean(context, value);
+}
+
+bool WebViewTest::javascriptResultIsNull(WebKitJavascriptResult* javascriptResult)
+{
+ JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
+ g_assert(context);
+ JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
+ g_assert(value);
+
+ return JSValueIsNull(context, value);
+}
+
+bool WebViewTest::javascriptResultIsUndefined(WebKitJavascriptResult* javascriptResult)
+{
+ JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
+ g_assert(context);
+ JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
+ g_assert(value);
+
+ return JSValueIsUndefined(context, value);
+}
+
+static void onSnapshotReady(WebKitWebView* web_view, GAsyncResult* res, WebViewTest* test)
+{
+ GUniqueOutPtr<GError> error;
+ test->m_surface = webkit_web_view_get_snapshot_finish(web_view, res, &error.outPtr());
+ g_assert(!test->m_surface || !error.get());
+ if (error)
+ g_assert_error(error.get(), WEBKIT_SNAPSHOT_ERROR, WEBKIT_SNAPSHOT_ERROR_FAILED_TO_CREATE);
+ test->quitMainLoop();
+}
+
+cairo_surface_t* WebViewTest::getSnapshotAndWaitUntilReady(WebKitSnapshotRegion region, WebKitSnapshotOptions options)
+{
+ if (m_surface)
+ cairo_surface_destroy(m_surface);
+ m_surface = 0;
+ webkit_web_view_get_snapshot(m_webView, region, options, 0, reinterpret_cast<GAsyncReadyCallback>(onSnapshotReady), this);
+ g_main_loop_run(m_mainLoop);
+ return m_surface;
+}
+
+bool WebViewTest::runWebProcessTest(const char* suiteName, const char* testName)
+{
+ GUniquePtr<char> script(g_strdup_printf("WebProcessTestRunner.runTest('%s/%s');", suiteName, testName));
+ GUniqueOutPtr<GError> error;
+ WebKitJavascriptResult* javascriptResult = runJavaScriptAndWaitUntilFinished(script.get(), &error.outPtr());
+ g_assert(!error);
+ return javascriptResultToBoolean(javascriptResult);
+}
diff --git a/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.h b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.h
new file mode 100644
index 000000000..3f99d21ec
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ * Portions Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WebViewTest_h
+#define WebViewTest_h
+
+#include "TestMain.h"
+#include <webkit2/webkit2.h>
+#include <wtf/text/CString.h>
+
+class WebViewTest: public Test {
+public:
+ MAKE_GLIB_TEST_FIXTURE(WebViewTest);
+ WebViewTest(WebKitUserContentManager* = nullptr);
+ virtual ~WebViewTest();
+
+ virtual void loadURI(const char* uri);
+ virtual void loadHtml(const char* html, const char* baseURI);
+ virtual void loadPlainText(const char* plainText);
+ virtual void loadRequest(WebKitURIRequest*);
+ virtual void loadBytes(GBytes*, const char* mimeType, const char* encoding, const char* baseURI);
+ void loadAlternateHTML(const char* html, const char* contentURI, const char* baseURI);
+ void goBack();
+ void goForward();
+ void goToBackForwardListItem(WebKitBackForwardListItem*);
+
+ void quitMainLoop();
+ void quitMainLoopAfterProcessingPendingEvents();
+ void wait(double seconds);
+ void waitUntilLoadFinished();
+ void waitUntilTitleChangedTo(const char* expectedTitle);
+ void waitUntilTitleChanged();
+ void showInWindow(GtkWindowType = GTK_WINDOW_POPUP);
+ void showInWindowAndWaitUntilMapped(GtkWindowType = GTK_WINDOW_POPUP, int width = 0, int height = 0);
+ void resizeView(int width, int height);
+ void selectAll();
+ const char* mainResourceData(size_t& mainResourceDataSize);
+
+ bool isEditable();
+ void setEditable(bool);
+
+ void mouseMoveTo(int x, int y, unsigned mouseModifiers = 0);
+ void clickMouseButton(int x, int y, unsigned button = 1, unsigned mouseModifiers = 0);
+ void keyStroke(unsigned keyVal, unsigned keyModifiers = 0);
+
+ WebKitJavascriptResult* runJavaScriptAndWaitUntilFinished(const char* javascript, GError**);
+ WebKitJavascriptResult* runJavaScriptFromGResourceAndWaitUntilFinished(const char* resource, GError**);
+
+ // Javascript result helpers.
+ static char* javascriptResultToCString(WebKitJavascriptResult*);
+ static double javascriptResultToNumber(WebKitJavascriptResult*);
+ static bool javascriptResultToBoolean(WebKitJavascriptResult*);
+ static bool javascriptResultIsNull(WebKitJavascriptResult*);
+ static bool javascriptResultIsUndefined(WebKitJavascriptResult*);
+
+ cairo_surface_t* getSnapshotAndWaitUntilReady(WebKitSnapshotRegion, WebKitSnapshotOptions);
+
+ bool runWebProcessTest(const char* suiteName, const char* testName);
+
+ // Prohibit overrides because this is called when the web view is created
+ // in our constructor, before a derived class's vtable is ready.
+ void initializeWebExtensions() override final { Test::initializeWebExtensions(); }
+
+ static gboolean webProcessCrashed(WebKitWebView*, WebViewTest*);
+
+ WebKitWebView* m_webView;
+ GMainLoop* m_mainLoop;
+ CString m_activeURI;
+ GtkWidget* m_parentWindow;
+ CString m_expectedTitle;
+ WebKitJavascriptResult* m_javascriptResult;
+ GError** m_javascriptError;
+ GUniquePtr<char> m_resourceData;
+ size_t m_resourceDataSize;
+ cairo_surface_t* m_surface;
+ bool m_expectedWebProcessCrash;
+
+private:
+ void doMouseButtonEvent(GdkEventType, int, int, unsigned, unsigned);
+};
+
+#endif // WebViewTest_h
diff --git a/Tools/TestWebKitAPI/gtk/main.cpp b/Tools/TestWebKitAPI/gtk/main.cpp
new file mode 100644
index 000000000..2c6856595
--- /dev/null
+++ b/Tools/TestWebKitAPI/gtk/main.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestsController.h"
+
+#include <gtk/gtk.h>
+
+int main(int argc, char** argv)
+{
+ gtk_init(&argc, &argv);
+
+ return TestWebKitAPI::TestsController::singleton().run(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/Tools/WebKitTestRunner/CMakeLists.txt b/Tools/WebKitTestRunner/CMakeLists.txt
new file mode 100644
index 000000000..0f537981d
--- /dev/null
+++ b/Tools/WebKitTestRunner/CMakeLists.txt
@@ -0,0 +1,108 @@
+set(WEBKIT_TESTRUNNER_DIR "${TOOLS_DIR}/WebKitTestRunner")
+set(WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR "${TOOLS_DIR}/WebKitTestRunner/InjectedBundle")
+
+file(MAKE_DIRECTORY ${DERIVED_SOURCES_DIR}/InjectedBundle)
+
+set(WebKitTestRunner_SOURCES
+ ${WEBKIT_TESTRUNNER_DIR}/CyclicRedundancyCheck.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/GeolocationProviderMock.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/Options.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/PixelDumpSupport.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/TestController.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/TestInvocation.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/WebNotificationProvider.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/WorkQueueManager.cpp
+)
+
+set(WebKitTestRunner_LIBRARIES
+ JavaScriptCore
+ WebCore
+ WebCoreTestSupport
+ WebKit2
+)
+
+set(WebKitTestRunner_INCLUDE_DIRECTORIES
+ ${WEBKIT_TESTRUNNER_DIR}
+ ${WEBKIT_TESTRUNNER_DIR}/InjectedBundle
+ ${WEBKIT_TESTRUNNER_DIR}/InjectedBundle/Bindings
+ ${WEBKIT_TESTRUNNER_DIR}/InjectedBundle/atk
+ ${JAVASCRIPTCORE_DIR}
+ ${JAVASCRIPTCORE_DIR}/ForwardingHeaders
+ ${DERIVED_SOURCES_JAVASCRIPTCORE_DIR}
+ ${WEBCORE_DIR}/editing
+ ${WEBCORE_DIR}/platform
+ ${WEBCORE_DIR}/platform/graphics
+ ${WEBCORE_DIR}/platform/graphics/harfbuzz
+ ${WEBCORE_DIR}/platform/graphics/harfbuzz/ng
+ ${WEBCORE_DIR}/platform/network
+ ${WEBCORE_DIR}/platform/text
+ ${WEBCORE_DIR}/testing/js
+ ${WEBKIT2_DIR}/Platform/IPC
+ ${WEBKIT2_DIR}/Shared
+ ${WEBKIT2_DIR}/Shared/API/c
+ ${WEBKIT2_DIR}/Shared/Plugins
+ ${WEBKIT2_DIR}/UIProcess
+ ${WEBKIT2_DIR}/UIProcess/API/C/soup
+ ${WEBKIT2_DIR}/WebProcess/InjectedBundle
+ ${WEBKIT2_DIR}/WebProcess/InjectedBundle/API/c
+ ${WTF_DIR}
+ ${DERIVED_SOURCES_DIR}/InjectedBundle
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/Source
+)
+
+set(WebKitTestRunner_SYSTEM_INCLUDE_DIRECTORIES
+ ${LIBSOUP_INCLUDE_DIRS}
+)
+
+set(WebKitTestRunnerInjectedBundle_SOURCES
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/AccessibilityController.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/AccessibilityTextMarker.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/AccessibilityTextMarkerRange.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/AccessibilityUIElement.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/EventSendingController.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/GCController.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/InjectedBundle.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/InjectedBundleMain.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/InjectedBundlePage.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/TestRunner.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/TextInputController.cpp
+
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings/JSWrapper.cpp
+)
+
+set(WebKitTestRunnerInjectedBundle_IDL_FILES
+ "${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings/AccessibilityController.idl"
+ "${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings/AccessibilityTextMarker.idl"
+ "${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings/AccessibilityTextMarkerRange.idl"
+ "${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings/AccessibilityUIElement.idl"
+ "${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings/EventSendingController.idl"
+ "${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings/GCController.idl"
+ "${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings/TestRunner.idl"
+ "${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings/TextInputController.idl"
+)
+
+set(WebKitTestRunnerInjectedBundle_LIBRARIES
+ ${WebKitTestRunner_LIBRARIES}
+)
+
+GENERATE_BINDINGS(WebKitTestRunnerInjectedBundle_SOURCES
+ "${WebKitTestRunnerInjectedBundle_IDL_FILES}"
+ "${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings"
+ "--include=${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/Bindings"
+ "${FEATURE_DEFINES_WITH_SPACE_SEPARATOR}"
+ ${DERIVED_SOURCES_DIR}/InjectedBundle JS TestRunner cpp
+ ${WEBCORE_DIR}/bindings/scripts/IDLAttributes.txt)
+
+WEBKIT_INCLUDE_CONFIG_FILES_IF_EXISTS()
+
+include_directories(${WebKitTestRunner_INCLUDE_DIRECTORIES})
+include_directories(SYSTEM ${WebKitTestRunner_SYSTEM_INCLUDE_DIRECTORIES})
+
+add_library(TestRunnerInjectedBundle SHARED ${WebKitTestRunnerInjectedBundle_SOURCES})
+target_link_libraries(TestRunnerInjectedBundle ${WebKitTestRunnerInjectedBundle_LIBRARIES})
+
+add_executable(WebKitTestRunner ${WebKitTestRunner_SOURCES})
+target_link_libraries(WebKitTestRunner ${WebKitTestRunner_LIBRARIES})
+
+add_dependencies(WebKit2 ${ForwardingHeadersForWebKitTestRunner_NAME})
diff --git a/Tools/WebKitTestRunner/CyclicRedundancyCheck.cpp b/Tools/WebKitTestRunner/CyclicRedundancyCheck.cpp
new file mode 100644
index 000000000..562225395
--- /dev/null
+++ b/Tools/WebKitTestRunner/CyclicRedundancyCheck.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010, Robert Eisele <robert@xarg.org> All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CyclicRedundancyCheck.h"
+
+#include <wtf/Vector.h>
+
+static void makeCrcTable(unsigned crcTable[256])
+{
+ for (unsigned i = 0; i < 256; i++) {
+ unsigned c = i;
+ for (int k = 0; k < 8; k++) {
+ if (c & 1)
+ c = -306674912 ^ ((c >> 1) & 0x7fffffff);
+ else
+ c = c >> 1;
+ }
+ crcTable[i] = c;
+ }
+}
+
+unsigned computeCrc(const Vector<unsigned char>& buffer)
+{
+ static unsigned crcTable[256];
+ static bool crcTableComputed = false;
+ if (!crcTableComputed) {
+ makeCrcTable(crcTable);
+ crcTableComputed = true;
+ }
+
+ unsigned crc = 0xffffffffU;
+ for (size_t i = 0; i < buffer.size(); ++i)
+ crc = crcTable[(crc ^ buffer[i]) & 0xff] ^ ((crc >> 8) & 0x00ffffffU);
+ return crc ^ 0xffffffffU;
+}
+
diff --git a/Tools/WebKitTestRunner/CyclicRedundancyCheck.h b/Tools/WebKitTestRunner/CyclicRedundancyCheck.h
new file mode 100644
index 000000000..ef1d78e86
--- /dev/null
+++ b/Tools/WebKitTestRunner/CyclicRedundancyCheck.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CyclicRedundancyCheck_h
+#define CyclicRedundancyCheck_h
+
+#include <wtf/Vector.h>
+
+unsigned computeCrc(const Vector<unsigned char>&);
+
+#endif
diff --git a/Tools/WebKitTestRunner/DerivedSources.make b/Tools/WebKitTestRunner/DerivedSources.make
new file mode 100644
index 000000000..d3f3308c7
--- /dev/null
+++ b/Tools/WebKitTestRunner/DerivedSources.make
@@ -0,0 +1,55 @@
+# Copyright (C) 2010 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+VPATH = \
+ $(WebKitTestRunner)/InjectedBundle/Bindings \
+#
+
+INTERFACES = \
+ AccessibilityController \
+ AccessibilityTextMarker \
+ AccessibilityTextMarkerRange \
+ AccessibilityUIElement \
+ EventSendingController \
+ GCController \
+ TestRunner \
+ TextInputController \
+#
+
+SCRIPTS = \
+ $(WebCoreScripts)/CodeGenerator.pm \
+ $(WebKitTestRunner)/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm \
+ $(WebCoreScripts)/IDLParser.pm \
+ $(WebCoreScripts)/generate-bindings.pl \
+#
+
+.PHONY : all
+
+JS%.h JS%.cpp : %.idl $(SCRIPTS)
+ @echo Generating bindings for $*...
+ @perl -I $(WebCoreScripts) -I $(WebKitTestRunner)/InjectedBundle/Bindings $(WebCoreScripts)/generate-bindings.pl --defines "" --include InjectedBundle/Bindings --outputDir . --generator TestRunner $<
+
+all : \
+ $(INTERFACES:%=JS%.h) \
+ $(INTERFACES:%=JS%.cpp) \
+#
diff --git a/Tools/WebKitTestRunner/EventSenderProxy.h b/Tools/WebKitTestRunner/EventSenderProxy.h
new file mode 100644
index 000000000..23110abb2
--- /dev/null
+++ b/Tools/WebKitTestRunner/EventSenderProxy.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef EventSenderProxy_h
+#define EventSenderProxy_h
+
+#include <wtf/Deque.h>
+#include <wtf/HashMap.h>
+#include <wtf/Vector.h>
+
+#if PLATFORM(GTK)
+#include <WebCore/GUniquePtrGtk.h>
+#include <gdk/gdk.h>
+#include <wtf/HashSet.h>
+#elif PLATFORM(EFL)
+#include "EWebKit2.h"
+#endif
+
+namespace WTR {
+
+class TestController;
+
+#if PLATFORM(GTK)
+struct WTREventQueueItem;
+#elif PLATFORM(EFL)
+struct WTREvent;
+#endif
+
+class EventSenderProxy {
+public:
+ explicit EventSenderProxy(TestController*);
+ ~EventSenderProxy();
+
+ WKPoint position() const { return m_position; }
+
+ void mouseDown(unsigned button, WKEventModifiers);
+ void mouseUp(unsigned button, WKEventModifiers);
+ void mouseForceDown();
+ void mouseForceUp();
+ void mouseForceChanged(float);
+ void mouseMoveTo(double x, double y);
+ void mouseScrollBy(int x, int y);
+ void mouseScrollByWithWheelAndMomentumPhases(int x, int y, int phase, int momentum);
+ void continuousMouseScrollBy(int x, int y, bool paged);
+
+ void leapForward(int milliseconds);
+
+ void keyDown(WKStringRef key, WKEventModifiers, unsigned location);
+
+#if ENABLE(TOUCH_EVENTS)
+ // Touch events.
+ void addTouchPoint(int x, int y);
+ void updateTouchPoint(int index, int x, int y);
+ void setTouchModifier(WKEventModifiers, bool enable);
+ void setTouchPointRadius(int radiusX, int radiusY);
+ void touchStart();
+ void touchMove();
+ void touchEnd();
+ void touchCancel();
+ void clearTouchPoints();
+ void releaseTouchPoint(int index);
+ void cancelTouchPoint(int index);
+#endif
+
+private:
+ TestController* m_testController;
+
+ double currentEventTime() { return m_time; }
+ void updateClickCountForButton(int button);
+
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ void replaySavedEvents();
+#endif
+
+#if PLATFORM(GTK)
+ void sendOrQueueEvent(GdkEvent*);
+ void dispatchEvent(GdkEvent*);
+ GdkEvent* createMouseButtonEvent(GdkEventType, unsigned button, WKEventModifiers);
+ GUniquePtr<GdkEvent> createTouchEvent(GdkEventType, int id);
+ void sendUpdatedTouchEvents();
+#elif PLATFORM(EFL)
+ void sendOrQueueEvent(const WTREvent&);
+ void dispatchEvent(const WTREvent&);
+#if ENABLE(TOUCH_EVENTS)
+ void sendTouchEvent(Ewk_Touch_Event_Type);
+#endif
+#endif
+
+ double m_time;
+ WKPoint m_position;
+ bool m_leftMouseButtonDown;
+ int m_clickCount;
+ double m_clickTime;
+ WKPoint m_clickPosition;
+ WKEventMouseButton m_clickButton;
+#if PLATFORM(COCOA)
+ int eventNumber;
+#elif PLATFORM(GTK)
+ Deque<WTREventQueueItem> m_eventQueue;
+ unsigned m_mouseButtonCurrentlyDown;
+ Vector<GUniquePtr<GdkEvent>> m_touchEvents;
+ HashSet<int> m_updatedTouchEvents;
+#elif PLATFORM(EFL)
+ Deque<WTREvent> m_eventQueue;
+ WKEventMouseButton m_mouseButton;
+#if ENABLE(TOUCH_EVENTS)
+ Eina_List* m_touchPoints;
+#endif
+#endif
+};
+
+} // namespace WTR
+
+#endif // EventSenderProxy_h
diff --git a/Tools/WebKitTestRunner/GeolocationProviderMock.cpp b/Tools/WebKitTestRunner/GeolocationProviderMock.cpp
new file mode 100644
index 000000000..ad4164966
--- /dev/null
+++ b/Tools/WebKitTestRunner/GeolocationProviderMock.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "GeolocationProviderMock.h"
+
+#include <WebKit/WKGeolocationManager.h>
+#include <string.h>
+#include <wtf/Assertions.h>
+#include <wtf/CurrentTime.h>
+
+namespace WTR {
+
+static void startUpdatingCallback(WKGeolocationManagerRef geolocationManager, const void* clientInfo)
+{
+ GeolocationProviderMock* geolocationProvider = static_cast<GeolocationProviderMock*>(const_cast<void*>(clientInfo));
+ geolocationProvider->startUpdating(geolocationManager);
+}
+
+static void stopUpdatingCallback(WKGeolocationManagerRef geolocationManager, const void* clientInfo)
+{
+ GeolocationProviderMock* geolocationProvider = static_cast<GeolocationProviderMock*>(const_cast<void*>(clientInfo));
+ geolocationProvider->stopUpdating(geolocationManager);
+}
+
+GeolocationProviderMock::GeolocationProviderMock(WKContextRef context)
+ : m_isActive(false)
+ , m_hasError(false)
+{
+ m_geolocationManager = WKContextGetGeolocationManager(context);
+
+ WKGeolocationProviderV1 providerCallback;
+ memset(&providerCallback, 0, sizeof(WKGeolocationProviderV1));
+ providerCallback.base.version = 1;
+ providerCallback.base.clientInfo = this;
+ providerCallback.startUpdating = startUpdatingCallback;
+ providerCallback.stopUpdating = stopUpdatingCallback;
+ WKGeolocationManagerSetProvider(m_geolocationManager, &providerCallback.base);
+}
+
+GeolocationProviderMock::~GeolocationProviderMock()
+{
+ WKGeolocationManagerSetProvider(m_geolocationManager, 0);
+}
+
+void GeolocationProviderMock::setPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed)
+{
+ m_position.adopt(WKGeolocationPositionCreate_b(currentTime(), latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed));
+
+ m_hasError = false;
+ m_errorMessage.clear();
+
+ sendPositionIfNeeded();
+}
+
+void GeolocationProviderMock::setPositionUnavailableError(WKStringRef errorMessage)
+{
+ m_errorMessage = errorMessage;
+ m_hasError = true;
+
+ m_position.clear();
+
+ sendErrorIfNeeded();
+}
+
+void GeolocationProviderMock::startUpdating(WKGeolocationManagerRef geolocationManager)
+{
+ ASSERT_UNUSED(geolocationManager, geolocationManager == m_geolocationManager);
+
+ m_isActive = true;
+ sendPositionIfNeeded();
+ sendErrorIfNeeded();
+}
+
+void GeolocationProviderMock::stopUpdating(WKGeolocationManagerRef geolocationManager)
+{
+ ASSERT_UNUSED(geolocationManager, geolocationManager == m_geolocationManager);
+
+ m_isActive = false;
+}
+
+void GeolocationProviderMock::sendPositionIfNeeded()
+{
+ if (m_isActive && m_position)
+ WKGeolocationManagerProviderDidChangePosition(m_geolocationManager, m_position.get());
+}
+
+void GeolocationProviderMock::sendErrorIfNeeded()
+{
+ if (m_isActive && m_hasError)
+ WKGeolocationManagerProviderDidFailToDeterminePositionWithErrorMessage(m_geolocationManager, m_errorMessage.get());
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/GeolocationProviderMock.h b/Tools/WebKitTestRunner/GeolocationProviderMock.h
new file mode 100644
index 000000000..802223ec3
--- /dev/null
+++ b/Tools/WebKitTestRunner/GeolocationProviderMock.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GeolocationProviderMock_h
+#define GeolocationProviderMock_h
+
+#include <WebKit/WKRetainPtr.h>
+
+namespace WTR {
+
+class GeolocationProviderMock {
+public:
+ GeolocationProviderMock(WKContextRef);
+ ~GeolocationProviderMock();
+
+ void setPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed);
+ void setPositionUnavailableError(WKStringRef errorMessage);
+
+ void startUpdating(WKGeolocationManagerRef);
+ void stopUpdating(WKGeolocationManagerRef);
+
+ bool isActive() const { return m_isActive; }
+
+private:
+ void sendPositionIfNeeded();
+ void sendErrorIfNeeded();
+
+ WKGeolocationManagerRef m_geolocationManager;
+ bool m_isActive;
+
+ WKRetainPtr<WKGeolocationPositionRef> m_position;
+
+ bool m_hasError;
+ WKRetainPtr<WKStringRef> m_errorMessage;
+};
+
+} // namespace WTR
+
+#endif // GeolocationProviderMock_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.cpp b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.cpp
new file mode 100644
index 000000000..8b223fe4c
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AccessibilityController.h"
+
+#include "AccessibilityUIElement.h"
+#include "InjectedBundle.h"
+#include "InjectedBundlePage.h"
+#include "JSAccessibilityController.h"
+
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <WebKit/WKBundle.h>
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundlePagePrivate.h>
+
+namespace WTR {
+
+PassRefPtr<AccessibilityController> AccessibilityController::create()
+{
+ return adoptRef(new AccessibilityController);
+}
+
+AccessibilityController::AccessibilityController()
+{
+}
+
+AccessibilityController::~AccessibilityController()
+{
+}
+
+void AccessibilityController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
+{
+ setProperty(context, windowObject, "accessibilityController", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
+}
+
+JSClassRef AccessibilityController::wrapperClass()
+{
+ return JSAccessibilityController::accessibilityControllerClass();
+}
+
+void AccessibilityController::enableEnhancedAccessibility(bool enable)
+{
+ WKAccessibilityEnableEnhancedAccessibility(enable);
+}
+
+bool AccessibilityController::enhancedAccessibilityEnabled()
+{
+ return WKAccessibilityEnhancedAccessibilityEnabled();
+}
+
+#if !PLATFORM(GTK) && !PLATFORM(EFL)
+PassRefPtr<AccessibilityUIElement> AccessibilityController::rootElement()
+{
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ void* root = WKAccessibilityRootObject(page);
+
+ return AccessibilityUIElement::create(static_cast<PlatformUIElement>(root));
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityController::focusedElement()
+{
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ void* root = WKAccessibilityFocusedObject(page);
+
+ return AccessibilityUIElement::create(static_cast<PlatformUIElement>(root));
+}
+#endif
+
+PassRefPtr<AccessibilityUIElement> AccessibilityController::elementAtPoint(int x, int y)
+{
+ RefPtr<AccessibilityUIElement> uiElement = rootElement();
+ return uiElement->elementAtPoint(x, y);
+}
+
+// Unsupported methods on various platforms.
+// As they're implemented on other platforms this list should be modified.
+#if (!PLATFORM(GTK) && !PLATFORM(COCOA) && !PLATFORM(EFL)) || !HAVE(ACCESSIBILITY)
+bool AccessibilityController::addNotificationListener(JSValueRef) { return false; }
+bool AccessibilityController::removeNotificationListener() { return false; }
+PassRefPtr<AccessibilityUIElement> AccessibilityController::accessibleElementById(JSStringRef attribute) { return nullptr; }
+void AccessibilityController::logAccessibilityEvents() { }
+void AccessibilityController::resetToConsistentState() { }
+JSRetainPtr<JSStringRef> AccessibilityController::platformName() { return JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString("")); }
+#endif
+
+#if !HAVE(ACCESSIBILITY) && (PLATFORM(GTK) || PLATFORM(EFL))
+PassRefPtr<AccessibilityUIElement> AccessibilityController::rootElement() { return nullptr; }
+PassRefPtr<AccessibilityUIElement> AccessibilityController::focusedElement() { return nullptr; }
+#endif
+
+} // namespace WTR
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h
new file mode 100644
index 000000000..e099544a1
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityController_h
+#define AccessibilityController_h
+
+#include "AccessibilityUIElement.h"
+#include "JSWrappable.h"
+#include <JavaScriptCore/JSObjectRef.h>
+#include <wtf/Platform.h>
+#if HAVE(ACCESSIBILITY) && (PLATFORM(GTK) || PLATFORM(EFL))
+#include "AccessibilityNotificationHandlerAtk.h"
+#endif
+
+namespace WTR {
+
+class AccessibilityController : public JSWrappable {
+public:
+ static PassRefPtr<AccessibilityController> create();
+ ~AccessibilityController();
+
+ void makeWindowObject(JSContextRef, JSObjectRef windowObject, JSValueRef* exception);
+ virtual JSClassRef wrapperClass();
+
+ // Enhanced accessibility.
+ void enableEnhancedAccessibility(bool);
+ bool enhancedAccessibilityEnabled();
+
+ JSRetainPtr<JSStringRef> platformName();
+
+ // Controller Methods - platform-independent implementations.
+ PassRefPtr<AccessibilityUIElement> rootElement();
+ PassRefPtr<AccessibilityUIElement> focusedElement();
+ PassRefPtr<AccessibilityUIElement> elementAtPoint(int x, int y);
+ PassRefPtr<AccessibilityUIElement> accessibleElementById(JSStringRef idAttribute);
+
+ bool addNotificationListener(JSValueRef functionCallback);
+ bool removeNotificationListener();
+
+ // Here for consistency with DRT. Not implemented because they don't do anything on the Mac.
+ void logFocusEvents() { }
+ void logValueChangeEvents() { }
+ void logScrollingStartEvents() { }
+ void logAccessibilityEvents();
+
+ void resetToConsistentState();
+
+private:
+ AccessibilityController();
+
+#if PLATFORM(COCOA) || PLATFORM(IOS)
+ RetainPtr<NotificationHandler> m_globalNotificationHandler;
+#endif
+
+#if HAVE(ACCESSIBILITY) && (PLATFORM(GTK) || PLATFORM(EFL))
+ RefPtr<AccessibilityNotificationHandler> m_globalNotificationHandler;
+#endif
+};
+
+} // namespace WTR
+
+#endif // AccessibilityController_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarker.cpp b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarker.cpp
new file mode 100644
index 000000000..051114db1
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarker.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AccessibilityTextMarker.h"
+
+#include "AccessibilityUIElement.h"
+#include "JSAccessibilityTextMarker.h"
+
+#include <JavaScriptCore/JSRetainPtr.h>
+
+namespace WTR {
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityTextMarker::create(PlatformTextMarker marker)
+{
+ return adoptRef(new AccessibilityTextMarker(marker));
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityTextMarker::create(const AccessibilityTextMarker& marker)
+{
+ return adoptRef(new AccessibilityTextMarker(marker));
+}
+
+AccessibilityTextMarker::AccessibilityTextMarker(PlatformTextMarker marker)
+ : m_textMarker(marker)
+{
+}
+
+AccessibilityTextMarker::AccessibilityTextMarker(const AccessibilityTextMarker& marker)
+ : JSWrappable()
+ , m_textMarker(marker.platformTextMarker())
+{
+}
+
+AccessibilityTextMarker::~AccessibilityTextMarker()
+{
+}
+
+PlatformTextMarker AccessibilityTextMarker::platformTextMarker() const
+{
+#if PLATFORM(COCOA)
+ return m_textMarker.get();
+#else
+ return m_textMarker;
+#endif
+}
+
+JSClassRef AccessibilityTextMarker::wrapperClass()
+{
+ return JSAccessibilityTextMarker::accessibilityTextMarkerClass();
+}
+
+} // namespace WTR
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarker.h b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarker.h
new file mode 100644
index 000000000..74341718c
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarker.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityTextMarker_h
+#define AccessibilityTextMarker_h
+
+#include "JSWrappable.h"
+#include <JavaScriptCore/JSObjectRef.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Platform.h>
+
+#if PLATFORM(COCOA)
+#include <wtf/RetainPtr.h>
+typedef CFTypeRef PlatformTextMarker;
+#else
+typedef void* PlatformTextMarker;
+#endif
+
+namespace WTR {
+
+class AccessibilityUIElement;
+
+class AccessibilityTextMarker : public JSWrappable {
+public:
+ static PassRefPtr<AccessibilityTextMarker> create(PlatformTextMarker);
+ static PassRefPtr<AccessibilityTextMarker> create(const AccessibilityTextMarker&);
+
+ ~AccessibilityTextMarker();
+
+ PlatformTextMarker platformTextMarker() const;
+ virtual JSClassRef wrapperClass();
+
+ static JSObjectRef makeJSAccessibilityTextMarker(JSContextRef, const AccessibilityTextMarker&);
+ bool isEqual(AccessibilityTextMarker*);
+
+private:
+ AccessibilityTextMarker(PlatformTextMarker);
+ AccessibilityTextMarker(const AccessibilityTextMarker&);
+
+#if PLATFORM(COCOA)
+ RetainPtr<PlatformTextMarker> m_textMarker;
+#else
+ PlatformTextMarker m_textMarker;
+#endif
+};
+
+#if !PLATFORM(COCOA)
+inline bool AccessibilityTextMarker::isEqual(AccessibilityTextMarker*) { return false; }
+#endif
+
+} // namespace WTR
+
+#endif // AccessibilityTextMarker_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarkerRange.cpp b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarkerRange.cpp
new file mode 100644
index 000000000..53d8bfbe8
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarkerRange.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AccessibilityTextMarker.h"
+
+#include "AccessibilityUIElement.h"
+#include "JSAccessibilityTextMarkerRange.h"
+
+#include <JavaScriptCore/JSRetainPtr.h>
+
+namespace WTR {
+
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityTextMarkerRange::create(PlatformTextMarkerRange markerRange)
+{
+ return adoptRef(new AccessibilityTextMarkerRange(markerRange));
+}
+
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityTextMarkerRange::create(const AccessibilityTextMarkerRange& markerRange)
+{
+ return adoptRef(new AccessibilityTextMarkerRange(markerRange));
+}
+
+AccessibilityTextMarkerRange::AccessibilityTextMarkerRange(PlatformTextMarkerRange markerRange)
+ : m_textMarkerRange(markerRange)
+{
+}
+
+AccessibilityTextMarkerRange::AccessibilityTextMarkerRange(const AccessibilityTextMarkerRange& markerRange)
+ : JSWrappable()
+ , m_textMarkerRange(markerRange.platformTextMarkerRange())
+{
+}
+
+AccessibilityTextMarkerRange::~AccessibilityTextMarkerRange()
+{
+}
+
+PlatformTextMarkerRange AccessibilityTextMarkerRange::platformTextMarkerRange() const
+{
+#if PLATFORM(COCOA)
+ return m_textMarkerRange.get();
+#else
+ return m_textMarkerRange;
+#endif
+}
+
+JSClassRef AccessibilityTextMarkerRange::wrapperClass()
+{
+ return JSAccessibilityTextMarkerRange::accessibilityTextMarkerRangeClass();
+}
+
+} // namespace WTR
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarkerRange.h b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarkerRange.h
new file mode 100644
index 000000000..d3e685b2d
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityTextMarkerRange.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityTextMarkerRange_h
+#define AccessibilityTextMarkerRange_h
+
+#include "JSWrappable.h"
+#include <JavaScriptCore/JSObjectRef.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Platform.h>
+
+#if PLATFORM(COCOA)
+#include <wtf/RetainPtr.h>
+typedef CFTypeRef PlatformTextMarkerRange;
+#else
+typedef void* PlatformTextMarkerRange;
+#endif
+
+namespace WTR {
+
+class AccessibilityTextMarkerRange : public JSWrappable {
+public:
+ static PassRefPtr<AccessibilityTextMarkerRange> create(PlatformTextMarkerRange);
+ static PassRefPtr<AccessibilityTextMarkerRange> create(const AccessibilityTextMarkerRange&);
+
+ ~AccessibilityTextMarkerRange();
+
+ PlatformTextMarkerRange platformTextMarkerRange() const;
+ virtual JSClassRef wrapperClass();
+
+ static JSObjectRef makeJSAccessibilityTextMarkerRange(JSContextRef, const AccessibilityTextMarkerRange&);
+ bool isEqual(AccessibilityTextMarkerRange*);
+
+private:
+ AccessibilityTextMarkerRange(PlatformTextMarkerRange);
+ AccessibilityTextMarkerRange(const AccessibilityTextMarkerRange&);
+
+#if PLATFORM(COCOA)
+ RetainPtr<PlatformTextMarkerRange> m_textMarkerRange;
+#else
+ PlatformTextMarkerRange m_textMarkerRange;
+#endif
+};
+
+#if !PLATFORM(COCOA)
+inline bool AccessibilityTextMarkerRange::isEqual(AccessibilityTextMarkerRange*) { return false; }
+#endif
+
+} // namespace WTR
+
+#endif // AccessibilityTextMarkerRange_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.cpp b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.cpp
new file mode 100644
index 000000000..425b8e2be
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AccessibilityUIElement.h"
+
+#include "JSAccessibilityUIElement.h"
+#include <JavaScriptCore/JSRetainPtr.h>
+
+namespace WTR {
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::create(PlatformUIElement uiElement)
+{
+ return adoptRef(new AccessibilityUIElement(uiElement));
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::create(const AccessibilityUIElement& uiElement)
+{
+ return adoptRef(new AccessibilityUIElement(uiElement));
+}
+
+JSClassRef AccessibilityUIElement::wrapperClass()
+{
+ return JSAccessibilityUIElement::accessibilityUIElementClass();
+}
+
+// Implementation
+
+bool AccessibilityUIElement::isValid() const
+{
+ return m_element;
+}
+
+// iOS specific methods
+#if !PLATFORM(IOS)
+JSRetainPtr<JSStringRef> AccessibilityUIElement::identifier() { return nullptr; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::traits() { return nullptr; }
+int AccessibilityUIElement::elementTextPosition() { return 0; }
+int AccessibilityUIElement::elementTextLength() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForSelection() { return nullptr; }
+JSValueRef AccessibilityUIElement::elementsForRange(unsigned, unsigned) { return nullptr; }
+void AccessibilityUIElement::increaseTextSelection() { }
+void AccessibilityUIElement::decreaseTextSelection() { }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedElement() { return nullptr; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::headerElementAtIndex(unsigned) { return nullptr; }
+void AccessibilityUIElement::assistiveTechnologySimulatedFocus() { return; }
+bool AccessibilityUIElement::scrollPageUp() { return false; }
+bool AccessibilityUIElement::scrollPageDown() { return false; }
+bool AccessibilityUIElement::scrollPageLeft() { return false; }
+bool AccessibilityUIElement::scrollPageRight() { return false; }
+#endif
+
+// Unsupported methods on various platforms. As they're implemented on other platforms this list should be modified.
+#if (!PLATFORM(GTK) && !PLATFORM(EFL)) || !HAVE(ACCESSIBILITY)
+JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int) { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int) { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int) { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int) { return 0; }
+void AccessibilityUIElement::setSelectedChildAtIndex(unsigned) const { }
+void AccessibilityUIElement::removeSelectionAtIndex(unsigned) const { }
+#endif
+
+#if (!PLATFORM(COCOA) && !PLATFORM(GTK) && !PLATFORM(EFL)) || !HAVE(ACCESSIBILITY)
+AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement) { }
+AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement&) { }
+AccessibilityUIElement::~AccessibilityUIElement() { }
+bool AccessibilityUIElement::isEqual(AccessibilityUIElement*) { return false; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::elementAtPoint(int, int) { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::childAtIndex(unsigned) { return 0; }
+unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement*) { return 0; }
+int AccessibilityUIElement::childrenCount() { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement() { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::parentElement() { return 0; }
+void AccessibilityUIElement::takeFocus() { }
+void AccessibilityUIElement::takeSelection() { }
+void AccessibilityUIElement::addSelection() { }
+void AccessibilityUIElement::removeSelection() { }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::allAttributes() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfLinkedUIElements() { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedUIElementAtIndex(unsigned) { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfDocumentLinks() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfChildren() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames() { return 0; }
+void AccessibilityUIElement::increment() { }
+void AccessibilityUIElement::decrement() { }
+void AccessibilityUIElement::showMenu() { }
+void AccessibilityUIElement::press() { }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::stringAttributeValue(JSStringRef) { return 0; }
+JSValueRef AccessibilityUIElement::uiElementArrayAttributeValue(JSStringRef) const { return nullptr; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementAttributeValue(JSStringRef) const { return 0; }
+double AccessibilityUIElement::numberAttributeValue(JSStringRef) { return 0; }
+bool AccessibilityUIElement::boolAttributeValue(JSStringRef) { return false; }
+bool AccessibilityUIElement::isAttributeSupported(JSStringRef) { return false; }
+bool AccessibilityUIElement::isAttributeSettable(JSStringRef) { return false; }
+bool AccessibilityUIElement::isPressActionSupported() { return false; }
+bool AccessibilityUIElement::isIncrementActionSupported() { return false; }
+bool AccessibilityUIElement::isDecrementActionSupported() { return false; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::role() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::computedRoleString() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::title() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::description() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::language() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const { return 0; }
+double AccessibilityUIElement::x() { return 0; }
+double AccessibilityUIElement::y() { return 0; }
+double AccessibilityUIElement::width() { return 0; }
+double AccessibilityUIElement::height() { return 0; }
+double AccessibilityUIElement::intValue() const { return 0; }
+double AccessibilityUIElement::minValue() { return 0; }
+double AccessibilityUIElement::maxValue() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription() { return 0; }
+int AccessibilityUIElement::insertionPointLineNumber() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange() { return 0; }
+bool AccessibilityUIElement::isEnabled() { return false; }
+bool AccessibilityUIElement::isRequired() const { return false; }
+bool AccessibilityUIElement::isFocused() const { return false; }
+bool AccessibilityUIElement::isFocusable() const { return false; }
+bool AccessibilityUIElement::isSelected() const { return false; }
+bool AccessibilityUIElement::isSelectedOptionActive() const { return false; }
+bool AccessibilityUIElement::isSelectable() const { return false; }
+bool AccessibilityUIElement::isMultiSelectable() const { return false; }
+void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement*) const { }
+unsigned AccessibilityUIElement::selectedChildrenCount() const { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedChildAtIndex(unsigned) const { return 0; }
+bool AccessibilityUIElement::isExpanded() const { return false; }
+bool AccessibilityUIElement::isChecked() const { return false; }
+bool AccessibilityUIElement::isIndeterminate() const { return false; }
+bool AccessibilityUIElement::isVisible() const { return false; }
+bool AccessibilityUIElement::isOffScreen() const { return false; }
+bool AccessibilityUIElement::isCollapsed() const { return false; }
+bool AccessibilityUIElement::isIgnored() const { return false; }
+bool AccessibilityUIElement::hasPopup() const { return false; }
+int AccessibilityUIElement::hierarchicalLevel() const { return 0; }
+double AccessibilityUIElement::clickPointX() { return 0; }
+double AccessibilityUIElement::clickPointY() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::url() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::speak() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader() { return 0; }
+int AccessibilityUIElement::indexInTable() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::rowIndexRange() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange() { return 0; }
+int AccessibilityUIElement::rowCount() { return 0; }
+int AccessibilityUIElement::columnCount() { return 0; }
+JSValueRef AccessibilityUIElement::rowHeaders() const { return nullptr; }
+JSValueRef AccessibilityUIElement::columnHeaders() const { return nullptr; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedRowAtIndex(unsigned) { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedByRow() { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedRowAtIndex(unsigned) { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::rowAtIndex(unsigned) { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned) { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned) { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaControlsElementAtIndex(unsigned) { return nullptr; }
+bool AccessibilityUIElement::ariaIsGrabbed() const { return false; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const { return 0; }
+int AccessibilityUIElement::lineForIndex(int) { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int) { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int, int) { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned, unsigned) { return 0; }
+bool AccessibilityUIElement::setSelectedTextRange(unsigned, unsigned) { return false; }
+bool AccessibilityUIElement::setSelectedVisibleTextRange(AccessibilityTextMarkerRange*) { return false; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned, unsigned) { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned, unsigned) { return 0; }
+bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned, unsigned) { return false; }
+unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef, AccessibilityUIElement*, bool, JSValueRef, JSStringRef, bool, bool) { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef, AccessibilityUIElement*, bool, JSValueRef, JSStringRef, bool, bool) { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::selectTextWithCriteria(JSContextRef, JSStringRef, JSValueRef, JSStringRef, JSStringRef) { return nullptr; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned, unsigned) { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const { return 0; }
+bool AccessibilityUIElement::addNotificationListener(JSValueRef) { return false; }
+bool AccessibilityUIElement::removeNotificationListener() { return false; }
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::lineTextMarkerRangeForTextMarker(AccessibilityTextMarker*) { return nullptr; }
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement*) { return 0; }
+int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange*) { return 0; }
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker*, AccessibilityTextMarker*) { return 0; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange*) { return 0; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange*) { return 0; }
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker*) { return 0; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height) { return 0; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height) { return 0; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int, int) { return 0; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker*) { return 0; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker*) { return 0; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker() { return 0; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker() { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange*) { return 0; }
+bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef, AccessibilityTextMarkerRange*) { return false; }
+int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker*) { return -1; }
+bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker*) { return false; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int) { return 0; }
+void AccessibilityUIElement::scrollToMakeVisible() { }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const { return 0; }
+JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const { return 0; }
+
+#endif
+
+#if !PLATFORM(MAC) || !HAVE(ACCESSIBILITY)
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::selectedTextMarkerRange() { return nullptr; }
+void AccessibilityUIElement::resetSelectedTextMarkerRange() { }
+void AccessibilityUIElement::setBoolAttributeValue(JSStringRef, bool) { }
+#endif
+
+} // namespace WTR
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h
new file mode 100644
index 000000000..029cb7f8a
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityUIElement_h
+#define AccessibilityUIElement_h
+
+#include "AccessibilityTextMarker.h"
+#include "AccessibilityTextMarkerRange.h"
+#include "JSWrappable.h"
+
+#include <JavaScriptCore/JSObjectRef.h>
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <wtf/Platform.h>
+#include <wtf/Vector.h>
+
+#if PLATFORM(COCOA)
+#ifdef __OBJC__
+typedef id PlatformUIElement;
+#else
+typedef struct objc_object* PlatformUIElement;
+#endif
+#elif HAVE(ACCESSIBILITY) && (PLATFORM(GTK) || PLATFORM(EFL))
+#include "AccessibilityNotificationHandlerAtk.h"
+#include <atk/atk.h>
+#include <wtf/glib/GRefPtr.h>
+typedef GRefPtr<AtkObject> PlatformUIElement;
+#else
+typedef void* PlatformUIElement;
+#endif
+
+#if PLATFORM(COCOA)
+#ifdef __OBJC__
+typedef id NotificationHandler;
+#else
+typedef struct objc_object* NotificationHandler;
+#endif
+#endif
+
+namespace WTR {
+
+class AccessibilityUIElement : public JSWrappable {
+public:
+ static PassRefPtr<AccessibilityUIElement> create(PlatformUIElement);
+ static PassRefPtr<AccessibilityUIElement> create(const AccessibilityUIElement&);
+
+ ~AccessibilityUIElement();
+
+ PlatformUIElement platformUIElement() { return m_element; }
+ virtual JSClassRef wrapperClass();
+
+ static JSObjectRef makeJSAccessibilityUIElement(JSContextRef, const AccessibilityUIElement&);
+
+ bool isEqual(AccessibilityUIElement* otherElement);
+
+ PassRefPtr<AccessibilityUIElement> elementAtPoint(int x, int y);
+ PassRefPtr<AccessibilityUIElement> childAtIndex(unsigned);
+ unsigned indexOfChild(AccessibilityUIElement*);
+ int childrenCount();
+ PassRefPtr<AccessibilityUIElement> titleUIElement();
+ PassRefPtr<AccessibilityUIElement> parentElement();
+
+ void takeFocus();
+ void takeSelection();
+ void addSelection();
+ void removeSelection();
+
+ // Methods - platform-independent implementations
+ JSRetainPtr<JSStringRef> allAttributes();
+ JSRetainPtr<JSStringRef> attributesOfLinkedUIElements();
+ PassRefPtr<AccessibilityUIElement> linkedUIElementAtIndex(unsigned);
+
+ JSRetainPtr<JSStringRef> attributesOfDocumentLinks();
+ JSRetainPtr<JSStringRef> attributesOfChildren();
+ JSRetainPtr<JSStringRef> parameterizedAttributeNames();
+ void increment();
+ void decrement();
+ void showMenu();
+ void press();
+
+ // Attributes - platform-independent implementations
+ JSRetainPtr<JSStringRef> stringAttributeValue(JSStringRef attribute);
+ double numberAttributeValue(JSStringRef attribute);
+ JSValueRef uiElementArrayAttributeValue(JSStringRef attribute) const;
+ PassRefPtr<AccessibilityUIElement> uiElementAttributeValue(JSStringRef attribute) const;
+ bool boolAttributeValue(JSStringRef attribute);
+ void setBoolAttributeValue(JSStringRef attribute, bool value);
+ bool isAttributeSupported(JSStringRef attribute);
+ bool isAttributeSettable(JSStringRef attribute);
+ bool isPressActionSupported();
+ bool isIncrementActionSupported();
+ bool isDecrementActionSupported();
+ JSRetainPtr<JSStringRef> role();
+ JSRetainPtr<JSStringRef> subrole();
+ JSRetainPtr<JSStringRef> roleDescription();
+ JSRetainPtr<JSStringRef> computedRoleString();
+ JSRetainPtr<JSStringRef> title();
+ JSRetainPtr<JSStringRef> description();
+ JSRetainPtr<JSStringRef> language();
+ JSRetainPtr<JSStringRef> stringValue();
+ JSRetainPtr<JSStringRef> accessibilityValue() const;
+ JSRetainPtr<JSStringRef> helpText() const;
+ JSRetainPtr<JSStringRef> orientation() const;
+ double x();
+ double y();
+ double width();
+ double height();
+ double intValue() const;
+ double minValue();
+ double maxValue();
+ JSRetainPtr<JSStringRef> valueDescription();
+ int insertionPointLineNumber();
+ JSRetainPtr<JSStringRef> selectedTextRange();
+ bool isEnabled();
+ bool isRequired() const;
+
+ bool isFocused() const;
+ bool isFocusable() const;
+ bool isSelected() const;
+ bool isSelectedOptionActive() const;
+ bool isSelectable() const;
+ bool isMultiSelectable() const;
+ void setSelectedChild(AccessibilityUIElement*) const;
+ void setSelectedChildAtIndex(unsigned) const;
+ void removeSelectionAtIndex(unsigned) const;
+ unsigned selectedChildrenCount() const;
+ PassRefPtr<AccessibilityUIElement> selectedChildAtIndex(unsigned) const;
+
+ bool isValid() const;
+ bool isExpanded() const;
+ bool isChecked() const;
+ bool isIndeterminate() const;
+ bool isVisible() const;
+ bool isOffScreen() const;
+ bool isCollapsed() const;
+ bool isIgnored() const;
+ bool hasPopup() const;
+ int hierarchicalLevel() const;
+ double clickPointX();
+ double clickPointY();
+ JSRetainPtr<JSStringRef> documentEncoding();
+ JSRetainPtr<JSStringRef> documentURI();
+ JSRetainPtr<JSStringRef> url();
+ JSRetainPtr<JSStringRef> classList() const;
+
+ // CSS3-speech properties.
+ JSRetainPtr<JSStringRef> speak();
+
+ // Table-specific attributes
+ JSRetainPtr<JSStringRef> attributesOfColumnHeaders();
+ JSRetainPtr<JSStringRef> attributesOfRowHeaders();
+ JSRetainPtr<JSStringRef> attributesOfColumns();
+ JSRetainPtr<JSStringRef> attributesOfRows();
+ JSRetainPtr<JSStringRef> attributesOfVisibleCells();
+ JSRetainPtr<JSStringRef> attributesOfHeader();
+ int indexInTable();
+ JSRetainPtr<JSStringRef> rowIndexRange();
+ JSRetainPtr<JSStringRef> columnIndexRange();
+ int rowCount();
+ int columnCount();
+ JSValueRef rowHeaders() const;
+ JSValueRef columnHeaders() const;
+
+ // Tree/Outline specific attributes
+ PassRefPtr<AccessibilityUIElement> selectedRowAtIndex(unsigned);
+ PassRefPtr<AccessibilityUIElement> disclosedByRow();
+ PassRefPtr<AccessibilityUIElement> disclosedRowAtIndex(unsigned);
+ PassRefPtr<AccessibilityUIElement> rowAtIndex(unsigned);
+
+ // ARIA specific
+ PassRefPtr<AccessibilityUIElement> ariaOwnsElementAtIndex(unsigned);
+ PassRefPtr<AccessibilityUIElement> ariaFlowToElementAtIndex(unsigned);
+ PassRefPtr<AccessibilityUIElement> ariaControlsElementAtIndex(unsigned);
+
+ // ARIA Drag and Drop
+ bool ariaIsGrabbed() const;
+ // A space concatentated string of all the drop effects.
+ JSRetainPtr<JSStringRef> ariaDropEffects() const;
+
+ // Parameterized attributes
+ int lineForIndex(int);
+ JSRetainPtr<JSStringRef> rangeForLine(int);
+ JSRetainPtr<JSStringRef> rangeForPosition(int x, int y);
+ JSRetainPtr<JSStringRef> boundsForRange(unsigned location, unsigned length);
+ bool setSelectedTextRange(unsigned location, unsigned length);
+ JSRetainPtr<JSStringRef> stringForRange(unsigned location, unsigned length);
+ JSRetainPtr<JSStringRef> attributedStringForRange(unsigned location, unsigned length);
+ bool attributedStringRangeIsMisspelled(unsigned location, unsigned length);
+ unsigned uiElementCountForSearchPredicate(JSContextRef, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly);
+ PassRefPtr<AccessibilityUIElement> uiElementForSearchPredicate(JSContextRef, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly);
+ JSRetainPtr<JSStringRef> selectTextWithCriteria(JSContextRef, JSStringRef ambiguityResolution, JSValueRef searchStrings, JSStringRef replacementString, JSStringRef activity);
+
+ // Text-specific
+ JSRetainPtr<JSStringRef> characterAtOffset(int offset);
+ JSRetainPtr<JSStringRef> wordAtOffset(int offset);
+ JSRetainPtr<JSStringRef> lineAtOffset(int offset);
+ JSRetainPtr<JSStringRef> sentenceAtOffset(int offset);
+
+ // Table-specific
+ PassRefPtr<AccessibilityUIElement> cellForColumnAndRow(unsigned column, unsigned row);
+
+ // Scrollarea-specific
+ PassRefPtr<AccessibilityUIElement> horizontalScrollbar() const;
+ PassRefPtr<AccessibilityUIElement> verticalScrollbar() const;
+
+ void scrollToMakeVisible();
+
+ // Text markers.
+ PassRefPtr<AccessibilityTextMarkerRange> lineTextMarkerRangeForTextMarker(AccessibilityTextMarker*);
+ PassRefPtr<AccessibilityTextMarkerRange> textMarkerRangeForElement(AccessibilityUIElement*);
+ PassRefPtr<AccessibilityTextMarkerRange> textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker);
+ PassRefPtr<AccessibilityTextMarkerRange> selectedTextMarkerRange();
+ void resetSelectedTextMarkerRange();
+ PassRefPtr<AccessibilityTextMarker> startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange*);
+ PassRefPtr<AccessibilityTextMarker> endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange*);
+ PassRefPtr<AccessibilityTextMarker> endTextMarkerForBounds(int x, int y, int width, int height);
+ PassRefPtr<AccessibilityTextMarker> startTextMarkerForBounds(int x, int y, int width, int height);
+ PassRefPtr<AccessibilityTextMarker> textMarkerForPoint(int x, int y);
+ PassRefPtr<AccessibilityTextMarker> previousTextMarker(AccessibilityTextMarker*);
+ PassRefPtr<AccessibilityTextMarker> nextTextMarker(AccessibilityTextMarker*);
+ PassRefPtr<AccessibilityUIElement> accessibilityElementForTextMarker(AccessibilityTextMarker*);
+ JSRetainPtr<JSStringRef> stringForTextMarkerRange(AccessibilityTextMarkerRange*);
+ int textMarkerRangeLength(AccessibilityTextMarkerRange*);
+ bool attributedStringForTextMarkerRangeContainsAttribute(JSStringRef, AccessibilityTextMarkerRange*);
+ int indexForTextMarker(AccessibilityTextMarker*);
+ bool isTextMarkerValid(AccessibilityTextMarker*);
+ PassRefPtr<AccessibilityTextMarker> textMarkerForIndex(int);
+ PassRefPtr<AccessibilityTextMarker> startTextMarker();
+ PassRefPtr<AccessibilityTextMarker> endTextMarker();
+ bool setSelectedVisibleTextRange(AccessibilityTextMarkerRange*);
+
+ // Returns an ordered list of supported actions for an element.
+ JSRetainPtr<JSStringRef> supportedActions() const;
+ JSRetainPtr<JSStringRef> mathPostscriptsDescription() const;
+ JSRetainPtr<JSStringRef> mathPrescriptsDescription() const;
+
+ JSRetainPtr<JSStringRef> pathDescription() const;
+
+ // Notifications
+ // Function callback should take one argument, the name of the notification.
+ bool addNotificationListener(JSValueRef functionCallback);
+ // Make sure you call remove, because you can't rely on objects being deallocated in a timely fashion.
+ bool removeNotificationListener();
+
+ JSRetainPtr<JSStringRef> identifier();
+ JSRetainPtr<JSStringRef> traits();
+ int elementTextPosition();
+ int elementTextLength();
+ JSRetainPtr<JSStringRef> stringForSelection();
+ JSValueRef elementsForRange(unsigned location, unsigned length);
+ void increaseTextSelection();
+ void decreaseTextSelection();
+ PassRefPtr<AccessibilityUIElement> linkedElement();
+ PassRefPtr<AccessibilityUIElement> headerElementAtIndex(unsigned index);
+ void assistiveTechnologySimulatedFocus();
+
+ bool scrollPageUp();
+ bool scrollPageDown();
+ bool scrollPageLeft();
+ bool scrollPageRight();
+
+private:
+ AccessibilityUIElement(PlatformUIElement);
+ AccessibilityUIElement(const AccessibilityUIElement&);
+
+ PlatformUIElement m_element;
+
+ // A retained, platform specific object used to help manage notifications for this object.
+#if HAVE(ACCESSIBILITY)
+#if PLATFORM(COCOA)
+ NotificationHandler m_notificationHandler;
+
+ void getLinkedUIElements(Vector<RefPtr<AccessibilityUIElement> >&);
+ void getDocumentLinks(Vector<RefPtr<AccessibilityUIElement> >&);
+
+ void getUIElementsWithAttribute(JSStringRef, Vector<RefPtr<AccessibilityUIElement> >&) const;
+#endif
+
+#if PLATFORM(COCOA) || PLATFORM(GTK) || PLATFORM(EFL)
+ void getChildren(Vector<RefPtr<AccessibilityUIElement> >&);
+ void getChildrenWithRange(Vector<RefPtr<AccessibilityUIElement> >&, unsigned location, unsigned length);
+#endif
+
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ RefPtr<AccessibilityNotificationHandler> m_notificationHandler;
+#endif
+#endif
+};
+
+} // namespace WTR
+
+#endif // AccessibilityUIElement_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/ActivateFonts.h b/Tools/WebKitTestRunner/InjectedBundle/ActivateFonts.h
new file mode 100644
index 000000000..5ee1276d4
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/ActivateFonts.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ActivateFonts_h
+#define ActivateFonts_h
+
+namespace WTR {
+
+void activateFonts();
+
+} // namespace WTR
+
+#endif // ActivateFonts_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityController.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityController.idl
new file mode 100644
index 000000000..3831154e0
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityController.idl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+interface AccessibilityController {
+ void enableEnhancedAccessibility(boolean enable);
+ readonly attribute boolean enhancedAccessibilityEnabled;
+
+ readonly attribute DOMString platformName;
+ readonly attribute AccessibilityUIElement rootElement;
+ readonly attribute AccessibilityUIElement focusedElement;
+ AccessibilityUIElement elementAtPoint(int x, int y);
+ AccessibilityUIElement accessibleElementById(DOMString id);
+
+ boolean addNotificationListener(object functionCallback);
+ boolean removeNotificationListener();
+
+ void logFocusEvents();
+ void logValueChangeEvents();
+ void logScrollingStartEvents();
+ void logAccessibilityEvents();
+ void resetToConsistentState();
+};
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityTextMarker.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityTextMarker.idl
new file mode 100644
index 000000000..149c95f34
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityTextMarker.idl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+interface AccessibilityTextMarker {
+ boolean isEqual(AccessibilityTextMarker otherMarker);
+};
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityTextMarkerRange.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityTextMarkerRange.idl
new file mode 100644
index 000000000..962c584e6
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityTextMarkerRange.idl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+interface AccessibilityTextMarkerRange {
+ boolean isEqual(AccessibilityTextMarkerRange otherMarkerRange);
+};
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl
new file mode 100644
index 000000000..d6d636305
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+interface AccessibilityUIElement {
+ boolean isEqual(AccessibilityUIElement otherElement);
+
+ // Document information
+ readonly attribute DOMString documentEncoding;
+ readonly attribute DOMString documentURI;
+
+ // Element access.
+ AccessibilityUIElement elementAtPoint(int x, int y);
+ AccessibilityUIElement childAtIndex(unsigned long index);
+ unsigned long indexOfChild(AccessibilityUIElement child);
+ AccessibilityUIElement linkedUIElementAtIndex(unsigned long index);
+ AccessibilityUIElement selectedChildAtIndex(unsigned long index);
+ void setSelectedChild(AccessibilityUIElement element);
+ void setSelectedChildAtIndex(unsigned long index);
+ void removeSelectionAtIndex(unsigned long index);
+ AccessibilityUIElement titleUIElement();
+ AccessibilityUIElement parentElement();
+
+ readonly attribute DOMString role;
+ readonly attribute DOMString subrole;
+ readonly attribute DOMString roleDescription;
+ readonly attribute DOMString computedRoleString;
+ readonly attribute DOMString title;
+ readonly attribute DOMString description;
+ readonly attribute DOMString language;
+ readonly attribute DOMString helpText;
+ readonly attribute DOMString valueDescription;
+ readonly attribute DOMString url;
+ readonly attribute DOMString speak;
+ readonly attribute DOMString orientation;
+ readonly attribute int insertionPointLineNumber;
+ readonly attribute DOMString selectedTextRange;
+
+ DOMString stringAttributeValue(DOMString attr);
+ double numberAttributeValue(DOMString attr);
+ object uiElementArrayAttributeValue(DOMString attr);
+ AccessibilityUIElement uiElementAttributeValue(DOMString attr);
+ boolean boolAttributeValue(DOMString attr);
+ void setBoolAttributeValue(DOMString attr, boolean value);
+ boolean isAttributeSupported(DOMString attr);
+ boolean isAttributeSettable(DOMString attr);
+ boolean isPressActionSupported();
+ boolean isIncrementActionSupported();
+ boolean isDecrementActionSupported();
+
+ readonly attribute DOMString stringValue;
+ readonly attribute int intValue;
+ readonly attribute int minValue;
+ readonly attribute int maxValue;
+
+ readonly attribute boolean isEnabled;
+ readonly attribute boolean isRequired;
+ readonly attribute boolean isFocused;
+ readonly attribute boolean isFocusable;
+ readonly attribute boolean isSelectable;
+ readonly attribute boolean isSelected;
+ readonly attribute boolean isSelectedOptionActive;
+ readonly attribute boolean isMultiSelectable;
+ readonly attribute boolean isExpanded;
+ readonly attribute boolean isChecked;
+ readonly attribute boolean isIndeterminate;
+ readonly attribute boolean isVisible;
+ readonly attribute boolean isCollapsed;
+ readonly attribute boolean hasPopup;
+ readonly attribute boolean isIgnored;
+ readonly attribute boolean isOffScreen;
+ readonly attribute boolean isValid;
+ readonly attribute int hierarchicalLevel;
+ readonly attribute boolean ariaIsGrabbed;
+ readonly attribute DOMString ariaDropEffects;
+ readonly attribute DOMString classList;
+
+ readonly attribute int x;
+ readonly attribute int y;
+ readonly attribute int width;
+ readonly attribute int height;
+ readonly attribute int clickPointX;
+ readonly attribute int clickPointY;
+
+ readonly attribute int childrenCount;
+ readonly attribute int selectedChildrenCount;
+ readonly attribute int rowCount;
+ readonly attribute int columnCount;
+
+ // Actions.
+ void increment();
+ void decrement();
+ void press();
+ void showMenu();
+
+ // Attribute info.
+ DOMString allAttributes();
+ DOMString attributesOfChildren();
+ DOMString attributesOfLinkedUIElements();
+ DOMString attributesOfDocumentLinks();
+
+ // Text info.
+ DOMString characterAtOffset(int offset);
+ DOMString wordAtOffset(int offset);
+ DOMString lineAtOffset(int offset);
+ DOMString sentenceAtOffset(int offset);
+
+ // Table info.
+ DOMString attributesOfColumnHeaders();
+ DOMString attributesOfRowHeaders();
+ DOMString attributesOfColumns();
+ DOMString attributesOfRows();
+ DOMString attributesOfVisibleCells();
+ DOMString attributesOfHeader();
+ AccessibilityUIElement cellForColumnAndRow(unsigned long column, unsigned long row);
+ AccessibilityUIElement selectedRowAtIndex(unsigned long index);
+ AccessibilityUIElement disclosedByRow();
+ AccessibilityUIElement disclosedRowAtIndex(unsigned long index);
+ AccessibilityUIElement rowAtIndex(unsigned long index);
+ int indexInTable();
+ DOMString rowIndexRange();
+ DOMString columnIndexRange();
+ int rowCount();
+ int columnCount();
+ object columnHeaders();
+ object rowHeaders();
+
+ AccessibilityUIElement ariaOwnsElementAtIndex(unsigned long index);
+ AccessibilityUIElement ariaFlowToElementAtIndex(unsigned long index);
+ AccessibilityUIElement ariaControlsElementAtIndex(unsigned long index);
+
+ // Paramaterized attributes.
+ DOMString parameterizedAttributeNames();
+ int lineForIndex(int index);
+ DOMString rangeForLine(int index);
+ DOMString rangeForPosition(int x, int y);
+ DOMString boundsForRange(unsigned long location, unsigned long length);
+ DOMString stringForRange(unsigned long location, unsigned long length);
+ DOMString attributedStringForRange(unsigned long location, unsigned long length);
+ boolean attributedStringRangeIsMisspelled(unsigned long location, unsigned long length);
+ [PassContext] unsigned int uiElementCountForSearchPredicate(AccessibilityUIElement startElement, boolean isDirectionNext, object searchKey, DOMString searchText, boolean visibleOnly, boolean immediateDescendantsOnly);
+ [PassContext] AccessibilityUIElement uiElementForSearchPredicate(AccessibilityUIElement startElement, boolean isDirectionNext, object searchKey, DOMString searchText, boolean visibleOnly, boolean immediateDescendantsOnly);
+ [PassContext] DOMString selectTextWithCriteria(DOMString ambiguityResolution, object searchStrings, DOMString replacementString, DOMString activity);
+ boolean setSelectedTextRange(unsigned long location, unsigned long length);
+
+ // Scroll area attributes.
+ readonly attribute AccessibilityUIElement horizontalScrollbar;
+ readonly attribute AccessibilityUIElement verticalScrollbar;
+
+ void scrollToMakeVisible();
+ void takeFocus();
+ boolean scrollPageDown();
+ boolean scrollPageUp();
+ boolean scrollPageLeft();
+ boolean scrollPageRight();
+
+ // Text markers.
+ AccessibilityTextMarkerRange lineTextMarkerRangeForTextMarker(AccessibilityTextMarker textMarker);
+ AccessibilityTextMarkerRange textMarkerRangeForElement(AccessibilityUIElement element);
+ AccessibilityTextMarkerRange textMarkerRangeForMarkers(AccessibilityTextMarker startMarker, AccessibilityTextMarker endMarker);
+ AccessibilityTextMarkerRange selectedTextMarkerRange();
+ void resetSelectedTextMarkerRange();
+ AccessibilityTextMarker startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange range);
+ AccessibilityTextMarker endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange range);
+ AccessibilityTextMarker endTextMarkerForBounds(int x, int y, int width, int height);
+ AccessibilityTextMarker startTextMarkerForBounds(int x, int y, int width, int height);
+ AccessibilityTextMarker textMarkerForPoint(int x, int y);
+ AccessibilityTextMarker previousTextMarker(AccessibilityTextMarker marker);
+ AccessibilityTextMarker nextTextMarker(AccessibilityTextMarker marker);
+ AccessibilityUIElement accessibilityElementForTextMarker(AccessibilityTextMarker marker);
+ DOMString stringForTextMarkerRange(AccessibilityTextMarkerRange range);
+ int textMarkerRangeLength(AccessibilityTextMarkerRange range);
+ boolean attributedStringForTextMarkerRangeContainsAttribute(DOMString attr, AccessibilityTextMarkerRange range);
+ int indexForTextMarker(AccessibilityTextMarker marker);
+ boolean isTextMarkerValid(AccessibilityTextMarker marker);
+ AccessibilityTextMarker textMarkerForIndex(int textIndex);
+ readonly attribute AccessibilityTextMarker startTextMarker;
+ readonly attribute AccessibilityTextMarker endTextMarker;
+ boolean setSelectedVisibleTextRange(AccessibilityTextMarkerRange range);
+
+ // Returns an ordered list of supported actions for an element.
+ readonly attribute DOMString supportedActions;
+ readonly attribute DOMString mathPostscriptsDescription;
+ readonly attribute DOMString mathPrescriptsDescription;
+
+ readonly attribute DOMString pathDescription;
+
+ // iOS specific accessibility methods.
+ readonly attribute DOMString identifier;
+ readonly attribute DOMString traits;
+ readonly attribute int elementTextPosition;
+ readonly attribute int elementTextLength;
+ readonly attribute DOMString stringForSelection;
+ object elementsForRange(unsigned long location, unsigned long length);
+ void increaseTextSelection();
+ void decreaseTextSelection();
+ AccessibilityUIElement linkedElement();
+ AccessibilityUIElement headerElementAtIndex(unsigned long index);
+ // This will simulate the accessibilityDidBecomeFocused API in UIKit.
+ void assistiveTechnologySimulatedFocus();
+
+ // Notification support.
+ boolean addNotificationListener(object callbackFunction);
+ boolean removeNotificationListener();
+};
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm b/Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm
new file mode 100644
index 000000000..19486c485
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm
@@ -0,0 +1,570 @@
+# Copyright (C) 2010 Apple Inc. All rights reserved.
+# Copyright (C) 2012 Samsung Electronics
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+use strict;
+use warnings;
+use File::Spec;
+
+package CodeGeneratorTestRunner;
+
+sub new
+{
+ my ($class, $codeGenerator, $writeDependencies, $verbose, $idlFilePath) = @_;
+
+ my $reference = {
+ codeGenerator => $codeGenerator,
+ idlFilePath => $idlFilePath,
+ };
+
+ bless($reference, $class);
+ return $reference;
+}
+
+sub GenerateInterface
+{
+}
+
+sub WriteData
+{
+ my ($self, $interface, $outputDir) = @_;
+
+ foreach my $file ($self->_generateHeaderFile($interface), $self->_generateImplementationFile($interface)) {
+ open(FILE, ">", File::Spec->catfile($outputDir, $$file{name})) or die "Failed to open $$file{name} for writing: $!";
+ print FILE @{$$file{contents}};
+ close(FILE) or die "Failed to close $$file{name} after writing: $!";
+ }
+}
+
+sub _className
+{
+ my ($idlType) = @_;
+
+ return "JS" . _implementationClassName($idlType);
+}
+
+sub _classRefGetter
+{
+ my ($self, $idlType) = @_;
+ return $$self{codeGenerator}->WK_lcfirst(_implementationClassName($idlType)) . "Class";
+}
+
+sub _parseLicenseBlock
+{
+ my ($fileHandle) = @_;
+
+ my ($copyright, $readCount, $buffer, $currentCharacter, $previousCharacter);
+ my $startSentinel = "/*";
+ my $lengthOfStartSentinel = length($startSentinel);
+ $readCount = read($fileHandle, $buffer, $lengthOfStartSentinel);
+ return "" if ($readCount < $lengthOfStartSentinel || $buffer ne $startSentinel);
+ $copyright = $buffer;
+
+ while ($readCount = read($fileHandle, $currentCharacter, 1)) {
+ $copyright .= $currentCharacter;
+ return $copyright if $currentCharacter eq "/" && $previousCharacter eq "*";
+ $previousCharacter = $currentCharacter;
+ }
+
+ return "";
+}
+
+sub _parseLicenseBlockFromFile
+{
+ my ($path) = @_;
+ open my $fileHandle, "<", $path or die "Failed to open $path for reading: $!";
+ my $licenseBlock = _parseLicenseBlock($fileHandle);
+ close($fileHandle);
+ return $licenseBlock;
+}
+
+sub _defaultLicenseBlock
+{
+ return <<EOF;
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+EOF
+}
+
+sub _licenseBlock
+{
+ my ($self) = @_;
+ return $self->{licenseBlock} if $self->{licenseBlock};
+
+ my $licenseBlock = _parseLicenseBlockFromFile($self->{idlFilePath}) || _defaultLicenseBlock();
+ $self->{licenseBlock} = $licenseBlock;
+ return $licenseBlock;
+}
+
+sub _generateHeaderFile
+{
+ my ($self, $interface) = @_;
+
+ my @contents = ();
+
+ my $idlType = $interface->name;
+ my $className = _className($idlType);
+ my $implementationClassName = _implementationClassName($idlType);
+ my $filename = $className . ".h";
+
+ push(@contents, $self->_licenseBlock());
+
+ my $parentClassName = _parentClassName($interface);
+
+ push(@contents, <<EOF);
+
+#ifndef ${className}_h
+#define ${className}_h
+
+#include "${parentClassName}.h"
+EOF
+ push(@contents, <<EOF);
+
+namespace WTR {
+
+class ${implementationClassName};
+
+class ${className} : public ${parentClassName} {
+public:
+ static JSClassRef @{[$self->_classRefGetter($idlType)]}();
+
+private:
+ static const JSStaticFunction* staticFunctions();
+ static const JSStaticValue* staticValues();
+EOF
+
+ if (my @functions = @{$interface->functions}) {
+ push(@contents, "\n // Functions\n\n");
+ foreach my $function (@functions) {
+ push(@contents, " static JSValueRef @{[$function->signature->name]}(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef*);\n");
+ }
+ }
+
+ if (my @attributes = @{$interface->attributes}) {
+ push(@contents, "\n // Attributes\n\n");
+ foreach my $attribute (@attributes) {
+ push(@contents, " static JSValueRef @{[$self->_getterName($attribute)]}(JSContextRef, JSObjectRef, JSStringRef, JSValueRef*);\n");
+ push(@contents, " static bool @{[$self->_setterName($attribute)]}(JSContextRef, JSObjectRef, JSStringRef, JSValueRef, JSValueRef*);\n") unless $attribute->isReadOnly;
+ }
+ }
+
+ push(@contents, <<EOF);
+};
+
+${implementationClassName}* to${implementationClassName}(JSContextRef, JSValueRef);
+
+} // namespace WTR
+
+#endif // ${className}_h
+EOF
+
+ return { name => $filename, contents => \@contents };
+}
+
+sub _generateImplementationFile
+{
+ my ($self, $interface) = @_;
+
+ my @contentsPrefix = ();
+ my %contentsIncludes = ();
+ my @contents = ();
+
+ my $idlType = $interface->name;
+ my $className = _className($idlType);
+ my $implementationClassName = _implementationClassName($idlType);
+ my $filename = $className . ".cpp";
+
+ push(@contentsPrefix, $self->_licenseBlock());
+
+ my $classRefGetter = $self->_classRefGetter($idlType);
+ my $parentClassName = _parentClassName($interface);
+
+ $contentsIncludes{"${className}.h"} = 1;
+ $contentsIncludes{"${implementationClassName}.h"} = 1;
+
+ push(@contentsPrefix, <<EOF);
+
+EOF
+
+ push(@contents, <<EOF);
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <wtf/GetPtr.h>
+
+namespace WTR {
+
+${implementationClassName}* to${implementationClassName}(JSContextRef context, JSValueRef value)
+{
+ if (!context || !value || !${className}::${classRefGetter}() || !JSValueIsObjectOfClass(context, value, ${className}::${classRefGetter}()))
+ return 0;
+ return static_cast<${implementationClassName}*>(JSWrapper::unwrap(context, value));
+}
+
+JSClassRef ${className}::${classRefGetter}()
+{
+ static JSClassRef jsClass;
+ if (!jsClass) {
+ JSClassDefinition definition = kJSClassDefinitionEmpty;
+ definition.className = "${idlType}";
+ definition.parentClass = @{[$self->_parentClassRefGetterExpression($interface)]};
+ definition.staticValues = staticValues();
+ definition.staticFunctions = staticFunctions();
+EOF
+
+ push(@contents, " definition.initialize = initialize;\n") unless _parentInterface($interface);
+ push(@contents, " definition.finalize = finalize;\n") unless _parentInterface($interface);
+
+ push(@contents, <<EOF);
+ jsClass = JSClassCreate(&definition);
+ }
+ return jsClass;
+}
+
+EOF
+
+ push(@contents, $self->_staticFunctionsGetterImplementation($interface), "\n");
+ push(@contents, $self->_staticValuesGetterImplementation($interface));
+
+ if (my @functions = @{$interface->functions}) {
+ push(@contents, "\n// Functions\n");
+
+ foreach my $function (@functions) {
+ push(@contents, <<EOF);
+
+JSValueRef ${className}::@{[$function->signature->name]}(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ ${implementationClassName}* impl = to${implementationClassName}(context, thisObject);
+ if (!impl)
+ return JSValueMakeUndefined(context);
+
+EOF
+ my $functionCall;
+ if ($function->signature->extendedAttributes->{"CustomArgumentHandling"}) {
+ $functionCall = "impl->" . $function->signature->name . "(context, argumentCount, arguments, exception)";
+ } else {
+ my @parameters = ();
+ my @specifiedParameters = @{$function->parameters};
+
+ $self->_includeHeaders(\%contentsIncludes, $function->signature->type, $function->signature);
+
+ if ($function->signature->extendedAttributes->{"PassContext"}) {
+ push(@parameters, "context");
+ }
+
+ foreach my $i (0..$#specifiedParameters) {
+ my $parameter = $specifiedParameters[$i];
+
+ $self->_includeHeaders(\%contentsIncludes, $idlType, $parameter);
+
+ push(@contents, " " . $self->_platformTypeVariableDeclaration($parameter, $parameter->name, "arguments[$i]", "argumentCount > $i") . "\n");
+
+ push(@parameters, $self->_parameterExpression($parameter));
+ }
+
+ $functionCall = "impl->" . $function->signature->name . "(" . join(", ", @parameters) . ")";
+ }
+
+ push(@contents, " ${functionCall};\n\n") if $function->signature->type eq "void";
+ push(@contents, " return " . $self->_returnExpression($function->signature, $functionCall) . ";\n}\n");
+ }
+ }
+
+ if (my @attributes = @{$interface->attributes}) {
+ push(@contents, "\n// Attributes\n");
+ foreach my $attribute (@attributes) {
+ $self->_includeHeaders(\%contentsIncludes, $attribute->signature->type, $attribute->signature);
+
+ my $getterName = $self->_getterName($attribute);
+ my $getterExpression = "impl->${getterName}()";
+
+ push(@contents, <<EOF);
+
+JSValueRef ${className}::${getterName}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef* exception)
+{
+ ${implementationClassName}* impl = to${implementationClassName}(context, object);
+ if (!impl)
+ return JSValueMakeUndefined(context);
+
+ return @{[$self->_returnExpression($attribute->signature, $getterExpression)]};
+}
+EOF
+
+ unless ($attribute->isReadOnly) {
+ push(@contents, <<EOF);
+
+bool ${className}::@{[$self->_setterName($attribute)]}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef value, JSValueRef* exception)
+{
+ ${implementationClassName}* impl = to${implementationClassName}(context, object);
+ if (!impl)
+ return false;
+
+EOF
+
+ my $platformValue = $self->_platformTypeConstructor($attribute->signature, "value");
+
+ push(@contents, <<EOF);
+ impl->@{[$self->_setterName($attribute)]}(${platformValue});
+
+ return true;
+}
+EOF
+ }
+ }
+ }
+
+ push(@contents, <<EOF);
+
+} // namespace WTR
+
+EOF
+
+ unshift(@contents, map { "#include \"$_\"\n" } sort keys(%contentsIncludes));
+ unshift(@contents, "#include \"config.h\"\n");
+ unshift(@contents, @contentsPrefix);
+
+ return { name => $filename, contents => \@contents };
+}
+
+sub _getterName
+{
+ my ($self, $attribute) = @_;
+
+ my $signature = $attribute->signature;
+ my $name = $signature->name;
+
+ return $name;
+}
+
+sub _includeHeaders
+{
+ my ($self, $headers, $idlType, $signature) = @_;
+
+ return unless defined $idlType;
+ return if $idlType eq "boolean";
+ return if $idlType eq "object";
+ return if $$self{codeGenerator}->IsNonPointerType($idlType);
+ return if $$self{codeGenerator}->IsStringType($idlType);
+
+ $$headers{_className($idlType) . ".h"} = 1;
+ $$headers{_implementationClassName($idlType) . ".h"} = 1;
+}
+
+sub _implementationClassName
+{
+ my ($idlType) = @_;
+
+ return $idlType;
+}
+
+sub _parentClassName
+{
+ my ($interface) = @_;
+
+ my $parentInterface = _parentInterface($interface);
+ return $parentInterface ? _className($parentInterface) : "JSWrapper";
+}
+
+sub _parentClassRefGetterExpression
+{
+ my ($self, $interface) = @_;
+
+ my $parentInterface = _parentInterface($interface);
+ return $parentInterface ? $self->_classRefGetter($parentInterface) . "()" : "0";
+}
+
+sub _parentInterface
+{
+ my ($interface) = @_;
+ return $interface->parent;
+}
+
+sub _platformType
+{
+ my ($self, $idlType, $signature) = @_;
+
+ return undef unless defined $idlType;
+
+ return "bool" if $idlType eq "boolean";
+ return "JSValueRef" if $idlType eq "object";
+ return "JSRetainPtr<JSStringRef>" if $$self{codeGenerator}->IsStringType($idlType);
+ return "double" if $$self{codeGenerator}->IsNonPointerType($idlType);
+ return _implementationClassName($idlType);
+}
+
+sub _platformTypeConstructor
+{
+ my ($self, $signature, $argumentName) = @_;
+
+ my $idlType = $signature->type;
+
+ return "JSValueToBoolean(context, $argumentName)" if $idlType eq "boolean";
+ return "$argumentName" if $idlType eq "object";
+ return "JSRetainPtr<JSStringRef>(Adopt, JSValueToStringCopy(context, $argumentName, 0))" if $$self{codeGenerator}->IsStringType($idlType);
+ return "JSValueToNumber(context, $argumentName, 0)" if $$self{codeGenerator}->IsNonPointerType($idlType);
+ return "to" . _implementationClassName($idlType) . "(context, $argumentName)";
+}
+
+sub _platformTypeVariableDeclaration
+{
+ my ($self, $signature, $variableName, $argumentName, $condition) = @_;
+
+ my $platformType = $self->_platformType($signature->type, $signature);
+ my $constructor = $self->_platformTypeConstructor($signature, $argumentName);
+
+ my %nonPointerTypes = (
+ "bool" => 1,
+ "double" => 1,
+ "JSRetainPtr<JSStringRef>" => 1,
+ "JSValueRef" => 1,
+ );
+
+ my $nullValue = "0";
+ if ($platformType eq "JSValueRef") {
+ $nullValue = "JSValueMakeUndefined(context)";
+ } elsif (defined $nonPointerTypes{$platformType} && $platformType ne "double") {
+ $nullValue = "$platformType()";
+ }
+
+ $platformType .= "*" unless defined $nonPointerTypes{$platformType};
+
+ return "$platformType $variableName = $condition && $constructor;" if $condition && $platformType eq "bool";
+ return "$platformType $variableName = $condition ? $constructor : $nullValue;" if $condition;
+ return "$platformType $variableName = $constructor;";
+}
+
+sub _returnExpression
+{
+ my ($self, $signature, $expression) = @_;
+
+ my $returnIDLType = $signature->type;
+
+ return "JSValueMakeUndefined(context)" if $returnIDLType eq "void";
+ return "JSValueMakeBoolean(context, ${expression})" if $returnIDLType eq "boolean";
+ return "${expression}" if $returnIDLType eq "object";
+ return "JSValueMakeNumber(context, ${expression})" if $$self{codeGenerator}->IsNonPointerType($returnIDLType);
+ return "JSValueMakeStringOrNull(context, ${expression}.get())" if $$self{codeGenerator}->IsStringType($returnIDLType);
+ return "toJS(context, WTF::getPtr(${expression}))";
+}
+
+sub _parameterExpression
+{
+ my ($self, $parameter) = @_;
+
+ my $idlType = $parameter->type;
+ my $name = $parameter->name;
+
+ return "${name}.get()" if $$self{codeGenerator}->IsStringType($idlType);
+ return $name;
+}
+
+sub _setterName
+{
+ my ($self, $attribute) = @_;
+
+ my $name = $attribute->signature->name;
+
+ return "set" . $$self{codeGenerator}->WK_ucfirst($name);
+}
+
+sub _staticFunctionsGetterImplementation
+{
+ my ($self, $interface) = @_;
+
+ my $mapFunction = sub {
+ my $name = $_->signature->name;
+ my @attributes = qw(kJSPropertyAttributeDontDelete kJSPropertyAttributeReadOnly);
+ push(@attributes, "kJSPropertyAttributeDontEnum") if $_->signature->extendedAttributes->{"DontEnum"};
+
+ return "{ \"$name\", $name, " . join(" | ", @attributes) . " }";
+ };
+
+ return $self->_staticFunctionsOrValuesGetterImplementation($interface, "function", "{ 0, 0, 0 }", $mapFunction, $interface->functions);
+}
+
+sub _staticFunctionsOrValuesGetterImplementation
+{
+ my ($self, $interface, $functionOrValue, $arrayTerminator, $mapFunction, $functionsOrAttributes) = @_;
+
+ my $className = _className($interface->name);
+ my $uppercaseFunctionOrValue = $$self{codeGenerator}->WK_ucfirst($functionOrValue);
+
+ my $result = <<EOF;
+const JSStatic${uppercaseFunctionOrValue}* ${className}::static${uppercaseFunctionOrValue}s()
+{
+EOF
+
+ my @initializers = map(&$mapFunction, @{$functionsOrAttributes});
+ return $result . " return 0;\n}\n" unless @initializers;
+
+ $result .= <<EOF
+ static const JSStatic${uppercaseFunctionOrValue} ${functionOrValue}s[] = {
+ @{[join(",\n ", @initializers)]},
+ ${arrayTerminator}
+ };
+ return ${functionOrValue}s;
+}
+EOF
+}
+
+sub _staticValuesGetterImplementation
+{
+ my ($self, $interface) = @_;
+
+ my $mapFunction = sub {
+ return if $_->signature->extendedAttributes->{"NoImplementation"};
+
+ my $attributeName = $_->signature->name;
+ my $getterName = $self->_getterName($_);
+ my $setterName = $_->isReadOnly ? "0" : $self->_setterName($_);
+ my @attributes = qw(kJSPropertyAttributeDontDelete);
+ push(@attributes, "kJSPropertyAttributeReadOnly") if $_->isReadOnly;
+ push(@attributes, "kJSPropertyAttributeDontEnum") if $_->signature->extendedAttributes->{"DontEnum"};
+
+ return "{ \"$attributeName\", $getterName, $setterName, " . join(" | ", @attributes) . " }";
+ };
+
+ return $self->_staticFunctionsOrValuesGetterImplementation($interface, "value", "{ 0, 0, 0, 0 }", $mapFunction, $interface->attributes);
+}
+
+1;
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/EventSendingController.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/EventSendingController.idl
new file mode 100644
index 000000000..56a91b1ba
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/EventSendingController.idl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010, 2011, 2014-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+interface EventSendingController {
+ void mouseDown(long buttonNumber, object modifierArray);
+ void mouseUp(long buttonNumber, object modifierArray);
+ void mouseMoveTo(long x, long y);
+ void mouseForceDown();
+ void mouseForceUp();
+ void mouseForceChanged(double force);
+ void mouseScrollBy(long x, long y);
+ void mouseScrollByWithWheelAndMomentumPhases(long x, long y, DOMString phase, DOMString momentum, optional boolean asyncScrolling);
+ void continuousMouseScrollBy(long x, long y, optional boolean paged);
+ object contextClick();
+ void scheduleAsynchronousClick();
+
+ void leapForward(long milliseconds);
+
+ void keyDown(DOMString key, object modifierArray, long location);
+ void scheduleAsynchronousKeyDown(DOMString key);
+
+ // Zoom functions.
+ void textZoomIn();
+ void textZoomOut();
+ void zoomPageIn();
+ void zoomPageOut();
+ void scalePageBy(double scale, double x, double y);
+
+ void monitorWheelEvents();
+ void callAfterScrollingCompletes(object functionCallback);
+
+#if defined(ENABLE_TOUCH_EVENTS) && ENABLE_TOUCH_EVENTS
+ // Touch events.
+ void addTouchPoint(long x, long y);
+ void updateTouchPoint(long index, long x, long y);
+ void setTouchModifier(DOMString modifier, boolean enable);
+ void setTouchPointRadius(long radiusX, long radiusY);
+ void touchStart();
+ void touchMove();
+ void touchEnd();
+ void touchCancel();
+ void clearTouchPoints();
+ void releaseTouchPoint(long index);
+ void cancelTouchPoint(long index);
+#endif
+};
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/GCController.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/GCController.idl
new file mode 100644
index 000000000..fb933834d
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/GCController.idl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+interface GCController {
+ void collect();
+ void collectOnAlternateThread(boolean waitUntilDone);
+ unsigned long long getJSObjectCount();
+};
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrappable.h b/Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrappable.h
new file mode 100644
index 000000000..5ec7197c5
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrappable.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSWrappable_h
+#define JSWrappable_h
+
+#include <JavaScriptCore/JavaScript.h>
+#include <wtf/RefCounted.h>
+
+namespace WTR {
+
+class JSWrappable : public RefCounted<JSWrappable> {
+public:
+ virtual ~JSWrappable() { }
+ virtual JSClassRef wrapperClass() = 0;
+};
+
+inline JSValueRef JSValueMakeStringOrNull(JSContextRef context, JSStringRef stringOrNull)
+{
+ return stringOrNull ? JSValueMakeString(context, stringOrNull) : JSValueMakeNull(context);
+}
+
+} // namespace WTR
+
+#endif // JSWrappable_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrapper.cpp b/Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrapper.cpp
new file mode 100644
index 000000000..e3cf58c2c
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrapper.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSWrapper.h"
+
+#include <JavaScriptCore/JSContextRefPrivate.h>
+
+namespace WTR {
+
+JSValueRef JSWrapper::wrap(JSContextRef context, JSWrappable* object)
+{
+ ASSERT_ARG(context, context);
+
+ if (!object)
+ return JSValueMakeNull(context);
+
+ JSClassRef objectClass = object->wrapperClass();
+ ASSERT(objectClass);
+ JSObjectRef wrapperObject = JSObjectMake(context, objectClass, object);
+ ASSERT(wrapperObject);
+
+ return wrapperObject;
+}
+
+JSWrappable* JSWrapper::unwrap(JSContextRef context, JSValueRef value)
+{
+ ASSERT_ARG(context, context);
+ ASSERT_ARG(value, value);
+ if (!context || !value)
+ return 0;
+ return static_cast<JSWrappable*>(JSObjectGetPrivate(JSValueToObject(context, value, 0)));
+}
+
+static JSWrappable* unwrapObject(JSObjectRef object)
+{
+ JSWrappable* wrappable = static_cast<JSWrappable*>(JSObjectGetPrivate(object));
+ ASSERT(wrappable);
+ return wrappable;
+}
+
+void JSWrapper::initialize(JSContextRef ctx, JSObjectRef object)
+{
+ JSWrappable* wrappable = unwrapObject(object);
+ if (!wrappable)
+ return;
+ wrappable->ref();
+}
+
+void JSWrapper::finalize(JSObjectRef object)
+{
+ JSWrappable* wrappable = unwrapObject(object);
+ if (!wrappable)
+ return;
+ wrappable->deref();
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrapper.h b/Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrapper.h
new file mode 100644
index 000000000..d885801b9
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrapper.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSWrapper_h
+#define JSWrapper_h
+
+#include "JSWrappable.h"
+#include <JavaScriptCore/JSRetainPtr.h>
+
+namespace WTR {
+
+// FIXME: If necessary, we can do wrapper caching here.
+class JSWrapper {
+public:
+ static JSValueRef wrap(JSContextRef context, JSWrappable* object);
+ static JSWrappable* unwrap(JSContextRef context, JSValueRef value);
+
+ static void initialize(JSContextRef, JSObjectRef);
+ static void finalize(JSObjectRef);
+};
+
+inline JSValueRef toJS(JSContextRef context, JSWrappable* impl)
+{
+ return JSWrapper::wrap(context, impl);
+}
+
+inline void setProperty(JSContextRef context, JSObjectRef object, const char* propertyName, JSWrappable* value, JSPropertyAttributes attributes, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName));
+ JSObjectSetProperty(context, object, propertyNameString.get(), JSWrapper::wrap(context, value), attributes, exception);
+}
+
+} // namespace WTR
+
+#endif // JSWrapper_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl
new file mode 100644
index 000000000..262544392
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2010, 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+interface TestRunner {
+ // The basics.
+ void dumpAsText(boolean dumpPixels);
+ void dumpChildFramesAsText();
+ void waitForPolicyDelegate();
+ void waitUntilDone();
+ void notifyDone();
+ double preciseTime();
+
+ // Other dumping.
+ void dumpBackForwardList();
+ void dumpChildFrameScrollPositions();
+ void dumpEditingCallbacks();
+ void dumpSelectionRect();
+ void dumpStatusCallbacks();
+ void dumpTitleChanges();
+ void dumpFullScreenCallbacks();
+ void dumpFrameLoadCallbacks();
+ void dumpProgressFinishedCallback();
+ void dumpResourceLoadCallbacks();
+ void dumpResourceResponseMIMETypes();
+ void dumpWillCacheResponse();
+ void dumpApplicationCacheDelegateCallbacks();
+ void dumpDatabaseCallbacks();
+ void dumpDOMAsWebArchive();
+ void dumpPolicyDelegateCallbacks();
+
+ // Special options.
+ void keepWebHistory();
+ void setAcceptsEditing(boolean value);
+ void setCanOpenWindows(boolean value);
+ void setCloseRemainingWindowsWhenComplete(boolean value);
+ void setXSSAuditorEnabled(boolean value);
+ void setAllowUniversalAccessFromFileURLs(boolean value);
+ void setAllowFileAccessFromFileURLs(boolean value);
+ void setPluginsEnabled(boolean value);
+ void setJavaScriptCanAccessClipboard(boolean value);
+ void setPrivateBrowsingEnabled(boolean value);
+ void setPopupBlockingEnabled(boolean value);
+ void setAuthorAndUserStylesEnabled(boolean value);
+ void setCustomPolicyDelegate(boolean enabled, boolean permissive);
+ void addOriginAccessWhitelistEntry(DOMString sourceOrigin, DOMString destinationProtocol, DOMString destinationHost, boolean allowDestinationSubdomains);
+ void removeOriginAccessWhitelistEntry(DOMString sourceOrigin, DOMString destinationProtocol, DOMString destinationHost, boolean allowDestinationSubdomains);
+ void setUserStyleSheetEnabled(boolean value);
+ void setUserStyleSheetLocation(DOMString location);
+ void setSpatialNavigationEnabled(boolean value);
+ void setTabKeyCyclesThroughElements(boolean enabled);
+ void setSerializeHTTPLoads();
+ void dispatchPendingLoadRequests();
+ void setCacheModel(int model);
+ void setAsynchronousSpellCheckingEnabled(boolean value);
+ void setPrinting();
+
+ // Special DOM functions.
+ void clearBackForwardList();
+ void execCommand(DOMString name, DOMString argument);
+ boolean isCommandEnabled(DOMString name);
+ unsigned long windowCount();
+
+ // Special DOM variables.
+ attribute boolean globalFlag;
+
+ // Repaint testing.
+ void testRepaint();
+ void repaintSweepHorizontally();
+ void display();
+
+ // Printing
+ boolean isPageBoxVisible(int pageIndex);
+
+ [PassContext] void setValueForUser(object element, DOMString value);
+
+ // UserContent testing.
+ void addUserScript(DOMString source, boolean runAtStart, boolean allFrames);
+ void addUserStyleSheet(DOMString source, boolean allFrames);
+
+ // Local storage API
+ void clearAllDatabases();
+ void setDatabaseQuota(unsigned long long quota);
+ DOMString pathToLocalResource(DOMString url);
+
+ attribute double databaseDefaultQuota;
+ attribute double databaseMaxQuota;
+
+ // Application Cache API
+ void clearAllApplicationCaches();
+ void setAppCacheMaximumSize(unsigned long long size);
+ long long applicationCacheDiskUsageForOrigin(DOMString origin);
+ void clearApplicationCacheForOrigin(DOMString name);
+ void disallowIncreaseForApplicationCacheQuota();
+ object originsWithApplicationCache();
+
+ // Text search testing.
+ boolean findString(DOMString target, object optionsArray);
+
+ // Evaluating script in a special context.
+ [PassContext] void evaluateScriptInIsolatedWorld(unsigned long worldID, DOMString script);
+
+ // For Web Inspector tests
+ void showWebInspector();
+ void closeWebInspector();
+ void evaluateInWebInspector(DOMString script);
+
+ void setPOSIXLocale(DOMString locale);
+
+ void setTextDirection(DOMString direction);
+
+ void setWillSendRequestReturnsNull(boolean flag);
+ void setWillSendRequestReturnsNullOnRedirect(boolean flag);
+
+ void setShouldStayOnPageAfterHandlingBeforeUnload(boolean flag);
+
+ void setDefersLoading(boolean flag);
+ void setStopProvisionalFrameLoads();
+
+ // Focus testing.
+ void addChromeInputField(object callback);
+ void removeChromeInputField(object callback);
+ void focusWebView(object callback);
+
+ void setBackingScaleFactor(double backingScaleFactor, object callback);
+
+ void setWindowIsKey(boolean isKey);
+
+ // Cookies testing
+ void setAlwaysAcceptCookies(boolean accept);
+
+ void overridePreference(DOMString preference, DOMString value);
+
+ // Page Visibility API
+ void setPageVisibility(DOMString state);
+ void resetPageVisibility();
+
+ // Control full screen behavior.
+ void setHasCustomFullScreenBehavior(boolean value);
+
+ // Web notifications support
+ void grantWebNotificationPermission(DOMString origin);
+ void denyWebNotificationPermission(DOMString origin);
+ void removeAllWebNotificationPermissions();
+ void simulateWebNotificationClick(object notification);
+
+ // Geolocation
+ void setGeolocationPermission(boolean value);
+ void setMockGeolocationPosition(double latitude, double longitude, double accuracy, [Default=Undefined] optional object altitude, optional object altitudeAccuracy, optional object heading, optional object speed);
+ void setMockGeolocationPositionUnavailableError(DOMString errorMessage);
+ boolean isGeolocationProviderActive();
+
+ // MediaStream
+ void setUserMediaPermission(boolean value);
+
+ // Audio testing.
+ [PassContext] void setAudioResult(object data);
+
+ boolean callShouldCloseOnWebView();
+
+ // Work queue.
+ void queueBackNavigation(unsigned long howFarBackward);
+ void queueForwardNavigation(unsigned long howFarForward);
+ void queueLoad(DOMString url, DOMString target, optional boolean shouldOpenExternalURLs);
+ void queueLoadHTMLString(DOMString content, optional DOMString baseURL, optional DOMString unreachableURL);
+ void queueReload();
+ void queueLoadingScript(DOMString script);
+ void queueNonLoadingScript(DOMString script);
+
+ // Authentication
+ void setHandlesAuthenticationChallenges(boolean value);
+ void setAuthenticationUsername(DOMString username);
+ void setAuthenticationPassword(DOMString password);
+
+ // Secure text input mode (Mac only)
+ readonly attribute boolean secureEventInputIsEnabled;
+
+ // Override plugin load policy.
+ void setBlockAllPlugins(boolean shouldBlock);
+
+ // Hooks to the JSC compiler.
+ object numberOfDFGCompiles(object function);
+ object neverInlineFunction(object function);
+};
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TextInputController.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TextInputController.idl
new file mode 100644
index 000000000..d95a6985d
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TextInputController.idl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+interface TextInputController {
+ void setMarkedText(DOMString string, long from, long length);
+ boolean hasMarkedText();
+ void unmarkText();
+ void insertText(DOMString string);
+};
+
diff --git a/Tools/WebKitTestRunner/InjectedBundle/EventSendingController.cpp b/Tools/WebKitTestRunner/InjectedBundle/EventSendingController.cpp
new file mode 100644
index 000000000..b9bc693a0
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/EventSendingController.cpp
@@ -0,0 +1,793 @@
+/*
+ * Copyright (C) 2010, 2011, 2014-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "EventSendingController.h"
+
+#include "InjectedBundle.h"
+#include "InjectedBundlePage.h"
+#include "JSEventSendingController.h"
+#include "StringFunctions.h"
+#include <WebKit/WKBundle.h>
+#include <WebKit/WKBundleFrame.h>
+#include <WebKit/WKBundlePagePrivate.h>
+#include <WebKit/WKBundlePrivate.h>
+#include <WebKit/WKContextMenuItem.h>
+#include <WebKit/WKMutableDictionary.h>
+#include <WebKit/WKNumber.h>
+#include <wtf/StdLibExtras.h>
+
+namespace WTR {
+
+static const float ZoomMultiplierRatio = 1.2f;
+
+struct MenuItemPrivateData {
+ MenuItemPrivateData(WKBundlePageRef page, WKContextMenuItemRef item) :
+ m_page(page),
+ m_item(item) { }
+ WKBundlePageRef m_page;
+ WKRetainPtr<WKContextMenuItemRef> m_item;
+};
+
+#if ENABLE(CONTEXT_MENUS)
+static JSValueRef menuItemClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ MenuItemPrivateData* privateData = static_cast<MenuItemPrivateData*>(JSObjectGetPrivate(thisObject));
+ WKBundlePageClickMenuItem(privateData->m_page, privateData->m_item.get());
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef getMenuItemTitleCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
+{
+ MenuItemPrivateData* privateData = static_cast<MenuItemPrivateData*>(JSObjectGetPrivate(object));
+ WKRetainPtr<WKStringRef> wkTitle(AdoptWK, WKContextMenuItemCopyTitle(privateData->m_item.get()));
+ return JSValueMakeString(context, toJS(wkTitle).get());
+}
+
+static JSStaticFunction staticMenuItemFunctions[] = {
+ { "click", menuItemClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { 0, 0, 0 }
+};
+
+static JSStaticValue staticMenuItemValues[] = {
+ { "title", getMenuItemTitleCallback, 0, kJSPropertyAttributeReadOnly },
+ { 0, 0, 0, 0 }
+};
+
+static void staticMenuItemFinalize(JSObjectRef object)
+{
+ delete static_cast<MenuItemPrivateData*>(JSObjectGetPrivate(object));
+}
+
+static JSValueRef staticConvertMenuItemToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception)
+{
+ if (kJSTypeString == type)
+ return getMenuItemTitleCallback(context, object, 0, 0);
+ return 0;
+}
+
+static JSClassRef getMenuItemClass()
+{
+ static JSClassRef menuItemClass = 0;
+
+ if (!menuItemClass) {
+ JSClassDefinition classDefinition = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ classDefinition.staticFunctions = staticMenuItemFunctions;
+ classDefinition.staticValues = staticMenuItemValues;
+ classDefinition.finalize = staticMenuItemFinalize;
+ classDefinition.convertToType = staticConvertMenuItemToType;
+
+ menuItemClass = JSClassCreate(&classDefinition);
+ }
+
+ return menuItemClass;
+}
+#endif
+
+static WKEventModifiers parseModifier(JSStringRef modifier)
+{
+ if (JSStringIsEqualToUTF8CString(modifier, "ctrlKey"))
+ return kWKEventModifiersControlKey;
+ if (JSStringIsEqualToUTF8CString(modifier, "shiftKey") || JSStringIsEqualToUTF8CString(modifier, "rangeSelectionKey"))
+ return kWKEventModifiersShiftKey;
+ if (JSStringIsEqualToUTF8CString(modifier, "altKey"))
+ return kWKEventModifiersAltKey;
+ if (JSStringIsEqualToUTF8CString(modifier, "metaKey"))
+ return kWKEventModifiersMetaKey;
+ if (JSStringIsEqualToUTF8CString(modifier, "addSelectionKey")) {
+#if OS(MAC_OS_X)
+ return kWKEventModifiersMetaKey;
+#else
+ return kWKEventModifiersControlKey;
+#endif
+ }
+ return 0;
+}
+
+static unsigned arrayLength(JSContextRef context, JSObjectRef array)
+{
+ JSRetainPtr<JSStringRef> lengthString(Adopt, JSStringCreateWithUTF8CString("length"));
+ JSValueRef lengthValue = JSObjectGetProperty(context, array, lengthString.get(), 0);
+ if (!lengthValue)
+ return 0;
+ return static_cast<unsigned>(JSValueToNumber(context, lengthValue, 0));
+}
+
+static WKEventModifiers parseModifierArray(JSContextRef context, JSValueRef arrayValue)
+{
+ if (!arrayValue)
+ return 0;
+
+ // The value may either be a string with a single modifier or an array of modifiers.
+ if (JSValueIsString(context, arrayValue)) {
+ JSRetainPtr<JSStringRef> string(Adopt, JSValueToStringCopy(context, arrayValue, 0));
+ return parseModifier(string.get());
+ }
+
+ if (!JSValueIsObject(context, arrayValue))
+ return 0;
+ JSObjectRef array = const_cast<JSObjectRef>(arrayValue);
+ unsigned length = arrayLength(context, array);
+ WKEventModifiers modifiers = 0;
+ for (unsigned i = 0; i < length; i++) {
+ JSValueRef exception = 0;
+ JSValueRef value = JSObjectGetPropertyAtIndex(context, array, i, &exception);
+ if (exception)
+ continue;
+ JSRetainPtr<JSStringRef> string(Adopt, JSValueToStringCopy(context, value, &exception));
+ if (exception)
+ continue;
+ modifiers |= parseModifier(string.get());
+ }
+ return modifiers;
+}
+
+PassRefPtr<EventSendingController> EventSendingController::create()
+{
+ return adoptRef(new EventSendingController);
+}
+
+EventSendingController::EventSendingController()
+{
+}
+
+EventSendingController::~EventSendingController()
+{
+}
+
+JSClassRef EventSendingController::wrapperClass()
+{
+ return JSEventSendingController::eventSendingControllerClass();
+}
+
+enum MouseState {
+ MouseUp,
+ MouseDown
+};
+
+static WKMutableDictionaryRef createMouseMessageBody(MouseState state, int button, WKEventModifiers modifiers)
+{
+ WKMutableDictionaryRef EventSenderMessageBody = WKMutableDictionaryCreate();
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString(state == MouseUp ? "MouseUp" : "MouseDown"));
+ WKDictionarySetItem(EventSenderMessageBody, subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
+ WKRetainPtr<WKUInt64Ref> buttonRef = adoptWK(WKUInt64Create(button));
+ WKDictionarySetItem(EventSenderMessageBody, buttonKey.get(), buttonRef.get());
+
+ WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
+ WKRetainPtr<WKUInt64Ref> modifiersRef = adoptWK(WKUInt64Create(modifiers));
+ WKDictionarySetItem(EventSenderMessageBody, modifiersKey.get(), modifiersRef.get());
+
+ return EventSenderMessageBody;
+}
+
+void EventSendingController::mouseDown(int button, JSValueRef modifierArray)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundlePageRef page = injectedBundle.page()->page();
+ WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(frame);
+ WKEventModifiers modifiers = parseModifierArray(context, modifierArray);
+
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, createMouseMessageBody(MouseDown, button, modifiers));
+
+ WKBundlePagePostSynchronousMessage(page, EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::mouseUp(int button, JSValueRef modifierArray)
+{
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(frame);
+ WKEventModifiers modifiers = parseModifierArray(context, modifierArray);
+
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, createMouseMessageBody(MouseUp, button, modifiers));
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::mouseMoveTo(int x, int y)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("MouseMoveTo"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> xKey(AdoptWK, WKStringCreateWithUTF8CString("X"));
+ WKRetainPtr<WKDoubleRef> xRef(AdoptWK, WKDoubleCreate(x));
+ WKDictionarySetItem(EventSenderMessageBody.get(), xKey.get(), xRef.get());
+
+ WKRetainPtr<WKStringRef> yKey(AdoptWK, WKStringCreateWithUTF8CString("Y"));
+ WKRetainPtr<WKDoubleRef> yRef(AdoptWK, WKDoubleCreate(y));
+ WKDictionarySetItem(EventSenderMessageBody.get(), yKey.get(), yRef.get());
+
+ m_position = WKPointMake(x, y);
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::mouseForceDown()
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("MouseForceDown"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::mouseForceUp()
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("MouseForceUp"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::mouseForceChanged(double force)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("MouseForceChanged"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> forceKey(AdoptWK, WKStringCreateWithUTF8CString("Force"));
+ WKRetainPtr<WKDoubleRef> forceRef(AdoptWK, WKDoubleCreate(force));
+ WKDictionarySetItem(EventSenderMessageBody.get(), forceKey.get(), forceRef.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::leapForward(int milliseconds)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("LeapForward"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> timeKey(AdoptWK, WKStringCreateWithUTF8CString("TimeInMilliseconds"));
+ WKRetainPtr<WKUInt64Ref> timeRef(AdoptWK, WKUInt64Create(milliseconds));
+ WKDictionarySetItem(EventSenderMessageBody.get(), timeKey.get(), timeRef.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::scheduleAsynchronousClick()
+{
+ WKEventModifiers modifiers = 0;
+ int button = 0;
+
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+
+ // Asynchronous mouse down.
+ WKRetainPtr<WKMutableDictionaryRef> mouseDownMessageBody(AdoptWK, createMouseMessageBody(MouseDown, button, modifiers));
+ WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), mouseDownMessageBody.get());
+
+ // Asynchronous mouse up.
+ WKRetainPtr<WKMutableDictionaryRef> mouseUpMessageBody(AdoptWK, createMouseMessageBody(MouseUp, button, modifiers));
+ WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), mouseUpMessageBody.get());
+}
+
+static WKRetainPtr<WKMutableDictionaryRef> createKeyDownMessageBody(JSStringRef key, WKEventModifiers modifiers, int location)
+{
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("KeyDown"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> keyKey(AdoptWK, WKStringCreateWithUTF8CString("Key"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), keyKey.get(), toWK(key).get());
+
+ WKRetainPtr<WKStringRef> modifiersKey(AdoptWK, WKStringCreateWithUTF8CString("Modifiers"));
+ WKRetainPtr<WKUInt64Ref> modifiersRef(AdoptWK, WKUInt64Create(modifiers));
+ WKDictionarySetItem(EventSenderMessageBody.get(), modifiersKey.get(), modifiersRef.get());
+
+ WKRetainPtr<WKStringRef> locationKey(AdoptWK, WKStringCreateWithUTF8CString("Location"));
+ WKRetainPtr<WKUInt64Ref> locationRef(AdoptWK, WKUInt64Create(location));
+ WKDictionarySetItem(EventSenderMessageBody.get(), locationKey.get(), locationRef.get());
+
+ return EventSenderMessageBody;
+}
+
+void EventSendingController::keyDown(JSStringRef key, JSValueRef modifierArray, int location)
+{
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(frame);
+ WKEventModifiers modifiers = parseModifierArray(context, modifierArray);
+
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> keyDownMessageBody = createKeyDownMessageBody(key, modifiers, location);
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), keyDownMessageBody.get(), 0);
+}
+
+void EventSendingController::scheduleAsynchronousKeyDown(JSStringRef key)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> keyDownMessageBody = createKeyDownMessageBody(key, 0 /* modifiers */, 0 /* location */);
+
+ WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), keyDownMessageBody.get());
+}
+
+void EventSendingController::mouseScrollBy(int x, int y)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("MouseScrollBy"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> xKey(AdoptWK, WKStringCreateWithUTF8CString("X"));
+ WKRetainPtr<WKDoubleRef> xRef(AdoptWK, WKDoubleCreate(x));
+ WKDictionarySetItem(EventSenderMessageBody.get(), xKey.get(), xRef.get());
+
+ WKRetainPtr<WKStringRef> yKey(AdoptWK, WKStringCreateWithUTF8CString("Y"));
+ WKRetainPtr<WKDoubleRef> yRef(AdoptWK, WKDoubleCreate(y));
+ WKDictionarySetItem(EventSenderMessageBody.get(), yKey.get(), yRef.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::mouseScrollByWithWheelAndMomentumPhases(int x, int y, JSStringRef phaseStr, JSStringRef momentumStr, bool asyncScrolling)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("MouseScrollByWithWheelAndMomentumPhases"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> xKey(AdoptWK, WKStringCreateWithUTF8CString("X"));
+ WKRetainPtr<WKDoubleRef> xRef(AdoptWK, WKDoubleCreate(x));
+ WKDictionarySetItem(EventSenderMessageBody.get(), xKey.get(), xRef.get());
+
+ WKRetainPtr<WKStringRef> yKey(AdoptWK, WKStringCreateWithUTF8CString("Y"));
+ WKRetainPtr<WKDoubleRef> yRef(AdoptWK, WKDoubleCreate(y));
+ WKDictionarySetItem(EventSenderMessageBody.get(), yKey.get(), yRef.get());
+
+ uint64_t phase = 0;
+ if (JSStringIsEqualToUTF8CString(phaseStr, "none"))
+ phase = 0;
+ else if (JSStringIsEqualToUTF8CString(phaseStr, "began"))
+ phase = 1; // kCGScrollPhaseBegan
+ else if (JSStringIsEqualToUTF8CString(phaseStr, "changed"))
+ phase = 2; // kCGScrollPhaseChanged
+ else if (JSStringIsEqualToUTF8CString(phaseStr, "ended"))
+ phase = 4; // kCGScrollPhaseEnded
+ else if (JSStringIsEqualToUTF8CString(phaseStr, "cancelled"))
+ phase = 8; // kCGScrollPhaseCancelled
+ else if (JSStringIsEqualToUTF8CString(phaseStr, "maybegin"))
+ phase = 128; // kCGScrollPhaseMayBegin
+
+ WKRetainPtr<WKStringRef> phaseKey(AdoptWK, WKStringCreateWithUTF8CString("Phase"));
+ WKRetainPtr<WKUInt64Ref> phaseRef(AdoptWK, WKUInt64Create(phase));
+ WKDictionarySetItem(EventSenderMessageBody.get(), phaseKey.get(), phaseRef.get());
+
+ uint64_t momentum = 0;
+ if (JSStringIsEqualToUTF8CString(momentumStr, "none"))
+ momentum = 0; // kCGMomentumScrollPhaseNone
+ else if (JSStringIsEqualToUTF8CString(momentumStr, "begin"))
+ momentum = 1; // kCGMomentumScrollPhaseBegin
+ else if (JSStringIsEqualToUTF8CString(momentumStr, "continue"))
+ momentum = 2; // kCGMomentumScrollPhaseContinue
+ else if (JSStringIsEqualToUTF8CString(momentumStr, "end"))
+ momentum = 3; // kCGMomentumScrollPhaseEnd
+
+ WKRetainPtr<WKStringRef> momentumKey(AdoptWK, WKStringCreateWithUTF8CString("Momentum"));
+ WKRetainPtr<WKUInt64Ref> momentumRef(AdoptWK, WKUInt64Create(momentum));
+ WKDictionarySetItem(EventSenderMessageBody.get(), momentumKey.get(), momentumRef.get());
+
+ if (asyncScrolling)
+ WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get());
+ else
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::continuousMouseScrollBy(int x, int y, bool paged)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("ContinuousMouseScrollBy"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> xKey(AdoptWK, WKStringCreateWithUTF8CString("X"));
+ WKRetainPtr<WKDoubleRef> xRef(AdoptWK, WKDoubleCreate(x));
+ WKDictionarySetItem(EventSenderMessageBody.get(), xKey.get(), xRef.get());
+
+ WKRetainPtr<WKStringRef> yKey(AdoptWK, WKStringCreateWithUTF8CString("Y"));
+ WKRetainPtr<WKDoubleRef> yRef(AdoptWK, WKDoubleCreate(y));
+ WKDictionarySetItem(EventSenderMessageBody.get(), yKey.get(), yRef.get());
+
+ WKRetainPtr<WKStringRef> pagedKey(AdoptWK, WKStringCreateWithUTF8CString("Paged"));
+ WKRetainPtr<WKUInt64Ref> pagedRef(AdoptWK, WKUInt64Create(paged));
+ WKDictionarySetItem(EventSenderMessageBody.get(), pagedKey.get(), pagedRef.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+JSValueRef EventSendingController::contextClick()
+{
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(page);
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+#if ENABLE(CONTEXT_MENUS)
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ // Do mouse context click.
+ mouseDown(2, 0);
+ mouseUp(2, 0);
+ WKRetainPtr<WKArrayRef> menuEntries = adoptWK(WKBundlePageCopyContextMenuItems(page));
+#else
+ WKRetainPtr<WKArrayRef> menuEntries = adoptWK(WKBundlePageCopyContextMenuAtPointInWindow(page, m_position));
+#endif
+ JSValueRef arrayResult = JSObjectMakeArray(context, 0, 0, 0);
+ JSObjectRef arrayObj = JSValueToObject(context, arrayResult, 0);
+ size_t entriesSize = WKArrayGetSize(menuEntries.get());
+ for (size_t i = 0; i < entriesSize; ++i) {
+ ASSERT(WKGetTypeID(WKArrayGetItemAtIndex(menuEntries.get(), i)) == WKContextMenuItemGetTypeID());
+
+ WKContextMenuItemRef item = static_cast<WKContextMenuItemRef>(WKArrayGetItemAtIndex(menuEntries.get(), i));
+ MenuItemPrivateData* privateData = new MenuItemPrivateData(page, item);
+ JSObjectSetPropertyAtIndex(context, arrayObj, i, JSObjectMake(context, getMenuItemClass(), privateData), 0);
+ }
+
+ return arrayResult;
+#else
+ return JSValueMakeUndefined(context);
+#endif
+}
+
+void EventSendingController::textZoomIn()
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ // Ensure page zoom is reset.
+ WKBundlePageSetPageZoomFactor(injectedBundle.page()->page(), 1);
+
+ double zoomFactor = WKBundlePageGetTextZoomFactor(injectedBundle.page()->page());
+ WKBundlePageSetTextZoomFactor(injectedBundle.page()->page(), zoomFactor * ZoomMultiplierRatio);
+}
+
+void EventSendingController::textZoomOut()
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ // Ensure page zoom is reset.
+ WKBundlePageSetPageZoomFactor(injectedBundle.page()->page(), 1);
+
+ double zoomFactor = WKBundlePageGetTextZoomFactor(injectedBundle.page()->page());
+ WKBundlePageSetTextZoomFactor(injectedBundle.page()->page(), zoomFactor / ZoomMultiplierRatio);
+}
+
+void EventSendingController::zoomPageIn()
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ // Ensure text zoom is reset.
+ WKBundlePageSetTextZoomFactor(injectedBundle.page()->page(), 1);
+
+ double zoomFactor = WKBundlePageGetPageZoomFactor(injectedBundle.page()->page());
+ WKBundlePageSetPageZoomFactor(injectedBundle.page()->page(), zoomFactor * ZoomMultiplierRatio);
+}
+
+void EventSendingController::zoomPageOut()
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ // Ensure text zoom is reset.
+ WKBundlePageSetTextZoomFactor(injectedBundle.page()->page(), 1);
+
+ double zoomFactor = WKBundlePageGetPageZoomFactor(injectedBundle.page()->page());
+ WKBundlePageSetPageZoomFactor(injectedBundle.page()->page(), zoomFactor / ZoomMultiplierRatio);
+}
+
+void EventSendingController::scalePageBy(double scale, double x, double y)
+{
+ WKPoint origin = { x, y };
+ WKBundlePageSetScaleAtOrigin(InjectedBundle::singleton().page()->page(), scale, origin);
+}
+
+void EventSendingController::monitorWheelEvents()
+{
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+
+ WKBundlePageStartMonitoringScrollOperations(page);
+}
+
+struct ScrollCompletionCallbackData {
+ JSContextRef m_context;
+ JSObjectRef m_function;
+
+ ScrollCompletionCallbackData(JSContextRef context, JSObjectRef function)
+ : m_context(context), m_function(function)
+ {
+ }
+};
+
+static void executeCallback(void* context)
+{
+ if (!context)
+ return;
+
+ std::unique_ptr<ScrollCompletionCallbackData> callBackData(reinterpret_cast<ScrollCompletionCallbackData*>(context));
+
+ JSObjectCallAsFunction(callBackData->m_context, callBackData->m_function, nullptr, 0, nullptr, nullptr);
+ JSValueUnprotect(callBackData->m_context, callBackData->m_function);
+}
+
+void EventSendingController::callAfterScrollingCompletes(JSValueRef functionCallback)
+{
+ if (!functionCallback)
+ return;
+
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(page);
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+
+ JSObjectRef functionCallbackObject = JSValueToObject(context, functionCallback, nullptr);
+ if (!functionCallbackObject)
+ return;
+
+ JSValueProtect(context, functionCallbackObject);
+
+ auto scrollCompletionCallbackData = std::make_unique<ScrollCompletionCallbackData>(context, functionCallbackObject);
+
+ WKBundlePageRegisterScrollOperationCompletionCallback(page, executeCallback, scrollCompletionCallbackData.release());
+}
+
+#if ENABLE(TOUCH_EVENTS)
+void EventSendingController::addTouchPoint(int x, int y)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("AddTouchPoint"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> xKey(AdoptWK, WKStringCreateWithUTF8CString("X"));
+ WKRetainPtr<WKUInt64Ref> xRef(AdoptWK, WKUInt64Create(x));
+ WKDictionarySetItem(EventSenderMessageBody.get(), xKey.get(), xRef.get());
+
+ WKRetainPtr<WKStringRef> yKey(AdoptWK, WKStringCreateWithUTF8CString("Y"));
+ WKRetainPtr<WKUInt64Ref> yRef(AdoptWK, WKUInt64Create(y));
+ WKDictionarySetItem(EventSenderMessageBody.get(), yKey.get(), yRef.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::updateTouchPoint(int index, int x, int y)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("UpdateTouchPoint"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> indexKey(AdoptWK, WKStringCreateWithUTF8CString("Index"));
+ WKRetainPtr<WKUInt64Ref> indexRef(AdoptWK, WKUInt64Create(index));
+ WKDictionarySetItem(EventSenderMessageBody.get(), indexKey.get(), indexRef.get());
+
+ WKRetainPtr<WKStringRef> xKey(AdoptWK, WKStringCreateWithUTF8CString("X"));
+ WKRetainPtr<WKUInt64Ref> xRef(AdoptWK, WKUInt64Create(x));
+ WKDictionarySetItem(EventSenderMessageBody.get(), xKey.get(), xRef.get());
+
+ WKRetainPtr<WKStringRef> yKey(AdoptWK, WKStringCreateWithUTF8CString("Y"));
+ WKRetainPtr<WKUInt64Ref> yRef(AdoptWK, WKUInt64Create(y));
+ WKDictionarySetItem(EventSenderMessageBody.get(), yKey.get(), yRef.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::setTouchModifier(const JSStringRef &modifier, bool enable)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("SetTouchModifier"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKEventModifiers mod = 0;
+ if (JSStringIsEqualToUTF8CString(modifier, "ctrl"))
+ mod = kWKEventModifiersControlKey;
+ if (JSStringIsEqualToUTF8CString(modifier, "shift"))
+ mod = kWKEventModifiersShiftKey;
+ if (JSStringIsEqualToUTF8CString(modifier, "alt"))
+ mod = kWKEventModifiersAltKey;
+ if (JSStringIsEqualToUTF8CString(modifier, "meta"))
+ mod = kWKEventModifiersMetaKey;
+
+ WKRetainPtr<WKStringRef> modifierKey(AdoptWK, WKStringCreateWithUTF8CString("Modifier"));
+ WKRetainPtr<WKUInt64Ref> modifierRef(AdoptWK, WKUInt64Create(mod));
+ WKDictionarySetItem(EventSenderMessageBody.get(), modifierKey.get(), modifierRef.get());
+
+ WKRetainPtr<WKStringRef> enableKey(AdoptWK, WKStringCreateWithUTF8CString("Enable"));
+ WKRetainPtr<WKUInt64Ref> enableRef(AdoptWK, WKUInt64Create(enable));
+ WKDictionarySetItem(EventSenderMessageBody.get(), enableKey.get(), enableRef.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+
+void EventSendingController::setTouchPointRadius(int radiusX, int radiusY)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("SetTouchPointRadius"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> xKey(AdoptWK, WKStringCreateWithUTF8CString("RadiusX"));
+ WKRetainPtr<WKUInt64Ref> xRef(AdoptWK, WKUInt64Create(radiusX));
+ WKDictionarySetItem(EventSenderMessageBody.get(), xKey.get(), xRef.get());
+
+ WKRetainPtr<WKStringRef> yKey(AdoptWK, WKStringCreateWithUTF8CString("RadiusY"));
+ WKRetainPtr<WKUInt64Ref> yRef(AdoptWK, WKUInt64Create(radiusY));
+ WKDictionarySetItem(EventSenderMessageBody.get(), yKey.get(), yRef.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::touchStart()
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("TouchStart"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::touchMove()
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("TouchMove"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::touchEnd()
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("TouchEnd"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::touchCancel()
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("TouchCancel"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::clearTouchPoints()
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("ClearTouchPoints"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::releaseTouchPoint(int index)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("ReleaseTouchPoint"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> indexKey(AdoptWK, WKStringCreateWithUTF8CString("Index"));
+ WKRetainPtr<WKUInt64Ref> indexRef(AdoptWK, WKUInt64Create(index));
+ WKDictionarySetItem(EventSenderMessageBody.get(), indexKey.get(), indexRef.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+
+void EventSendingController::cancelTouchPoint(int index)
+{
+ WKRetainPtr<WKStringRef> EventSenderMessageName(AdoptWK, WKStringCreateWithUTF8CString("EventSender"));
+ WKRetainPtr<WKMutableDictionaryRef> EventSenderMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKRetainPtr<WKStringRef> subMessageName(AdoptWK, WKStringCreateWithUTF8CString("CancelTouchPoint"));
+ WKDictionarySetItem(EventSenderMessageBody.get(), subMessageKey.get(), subMessageName.get());
+
+ WKRetainPtr<WKStringRef> indexKey(AdoptWK, WKStringCreateWithUTF8CString("Index"));
+ WKRetainPtr<WKUInt64Ref> indexRef(AdoptWK, WKUInt64Create(index));
+ WKDictionarySetItem(EventSenderMessageBody.get(), indexKey.get(), indexRef.get());
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), EventSenderMessageName.get(), EventSenderMessageBody.get(), 0);
+}
+#endif
+
+// Object Creation
+
+void EventSendingController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
+{
+ setProperty(context, windowObject, "eventSender", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/InjectedBundle/EventSendingController.h b/Tools/WebKitTestRunner/InjectedBundle/EventSendingController.h
new file mode 100644
index 000000000..ed77da1d3
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/EventSendingController.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010, 2011, 2014-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef EventSendingController_h
+#define EventSendingController_h
+
+#include "JSWrappable.h"
+#include <WebKit/WKEvent.h>
+#include <WebKit/WKGeometry.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WTR {
+
+class EventSendingController : public JSWrappable {
+public:
+ static PassRefPtr<EventSendingController> create();
+ virtual ~EventSendingController();
+
+ void makeWindowObject(JSContextRef, JSObjectRef windowObject, JSValueRef* exception);
+
+ // JSWrappable
+ virtual JSClassRef wrapperClass();
+
+ void mouseDown(int button, JSValueRef modifierArray);
+ void mouseUp(int button, JSValueRef modifierArray);
+ void mouseMoveTo(int x, int y);
+ void mouseForceDown();
+ void mouseForceUp();
+ void mouseForceChanged(double force);
+ void mouseScrollBy(int x, int y);
+ void mouseScrollByWithWheelAndMomentumPhases(int x, int y, JSStringRef phase, JSStringRef momentum, bool asyncScrolling);
+ void continuousMouseScrollBy(int x, int y, bool paged);
+ JSValueRef contextClick();
+ void leapForward(int milliseconds);
+ void scheduleAsynchronousClick();
+ void monitorWheelEvents();
+ void callAfterScrollingCompletes(JSValueRef functionCallback);
+
+ void keyDown(JSStringRef key, JSValueRef modifierArray, int location);
+ void scheduleAsynchronousKeyDown(JSStringRef key);
+
+ // Zoom functions.
+ void textZoomIn();
+ void textZoomOut();
+ void zoomPageIn();
+ void zoomPageOut();
+ void scalePageBy(double scale, double x, double y);
+
+#if ENABLE(TOUCH_EVENTS)
+ // Touch events.
+ void addTouchPoint(int x, int y);
+ void updateTouchPoint(int index, int x, int y);
+ void setTouchModifier(const JSStringRef &modifier, bool enable);
+ void setTouchPointRadius(int radiusX, int radiusY);
+ void touchStart();
+ void touchMove();
+ void touchEnd();
+ void touchCancel();
+ void clearTouchPoints();
+ void releaseTouchPoint(int index);
+ void cancelTouchPoint(int index);
+#endif
+
+private:
+ EventSendingController();
+ WKPoint m_position;
+};
+
+} // namespace WTR
+
+#endif // EventSendingController_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/GCController.cpp b/Tools/WebKitTestRunner/InjectedBundle/GCController.cpp
new file mode 100644
index 000000000..fd0cc3a78
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/GCController.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "GCController.h"
+
+#include "InjectedBundle.h"
+#include "JSGCController.h"
+#include <WebKit/WKBundlePrivate.h>
+
+namespace WTR {
+
+PassRefPtr<GCController> GCController::create()
+{
+ return adoptRef(new GCController);
+}
+
+GCController::GCController()
+{
+}
+
+GCController::~GCController()
+{
+}
+
+JSClassRef GCController::wrapperClass()
+{
+ return JSGCController::gCControllerClass();
+}
+
+void GCController::collect()
+{
+ WKBundleGarbageCollectJavaScriptObjects(InjectedBundle::singleton().bundle());
+}
+
+void GCController::collectOnAlternateThread(bool waitUntilDone)
+{
+ WKBundleGarbageCollectJavaScriptObjectsOnAlternateThreadForDebugging(InjectedBundle::singleton().bundle(), waitUntilDone);
+}
+
+size_t GCController::getJSObjectCount()
+{
+ return WKBundleGetJavaScriptObjectsCount(InjectedBundle::singleton().bundle());
+}
+
+// Object Creation
+
+void GCController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
+{
+ setProperty(context, windowObject, "GCController", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/InjectedBundle/GCController.h b/Tools/WebKitTestRunner/InjectedBundle/GCController.h
new file mode 100644
index 000000000..760fbb1e6
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/GCController.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCController_h
+#define GCController_h
+
+#include "JSWrappable.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WTR {
+
+class GCController : public JSWrappable {
+public:
+ static PassRefPtr<GCController> create();
+ virtual ~GCController();
+
+ void makeWindowObject(JSContextRef, JSObjectRef windowObject, JSValueRef* exception);
+
+ // JSWrappable
+ virtual JSClassRef wrapperClass();
+
+ void collect();
+ void collectOnAlternateThread(bool waitUntilDone);
+ size_t getJSObjectCount();
+
+private:
+ GCController();
+};
+
+} // namespace WTR
+
+#endif // GCController_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp
new file mode 100644
index 000000000..7e7732244
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2010, 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InjectedBundle.h"
+
+#include "ActivateFonts.h"
+#include "InjectedBundlePage.h"
+#include "StringFunctions.h"
+#include <WebKit/WKBundle.h>
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundlePagePrivate.h>
+#include <WebKit/WKBundlePrivate.h>
+#include <WebKit/WKRetainPtr.h>
+#include <WebKit/WebKit2_C.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/Vector.h>
+
+namespace WTR {
+
+InjectedBundle& InjectedBundle::singleton()
+{
+ static InjectedBundle& shared = *new InjectedBundle;
+ return shared;
+}
+
+InjectedBundle::InjectedBundle()
+ : m_bundle(0)
+ , m_topLoadingFrame(0)
+ , m_state(Idle)
+ , m_dumpPixels(false)
+ , m_useWaitToDumpWatchdogTimer(true)
+ , m_useWorkQueue(false)
+ , m_timeout(0)
+{
+}
+
+void InjectedBundle::didCreatePage(WKBundleRef bundle, WKBundlePageRef page, const void* clientInfo)
+{
+ static_cast<InjectedBundle*>(const_cast<void*>(clientInfo))->didCreatePage(page);
+}
+
+void InjectedBundle::willDestroyPage(WKBundleRef bundle, WKBundlePageRef page, const void* clientInfo)
+{
+ static_cast<InjectedBundle*>(const_cast<void*>(clientInfo))->willDestroyPage(page);
+}
+
+void InjectedBundle::didInitializePageGroup(WKBundleRef bundle, WKBundlePageGroupRef pageGroup, const void* clientInfo)
+{
+ static_cast<InjectedBundle*>(const_cast<void*>(clientInfo))->didInitializePageGroup(pageGroup);
+}
+
+void InjectedBundle::didReceiveMessage(WKBundleRef bundle, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ static_cast<InjectedBundle*>(const_cast<void*>(clientInfo))->didReceiveMessage(messageName, messageBody);
+}
+
+void InjectedBundle::didReceiveMessageToPage(WKBundleRef bundle, WKBundlePageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ static_cast<InjectedBundle*>(const_cast<void*>(clientInfo))->didReceiveMessageToPage(page, messageName, messageBody);
+}
+
+void InjectedBundle::initialize(WKBundleRef bundle, WKTypeRef initializationUserData)
+{
+ m_bundle = bundle;
+
+ WKBundleClientV1 client = {
+ { 1, this },
+ didCreatePage,
+ willDestroyPage,
+ didInitializePageGroup,
+ didReceiveMessage,
+ didReceiveMessageToPage
+ };
+ WKBundleSetClient(m_bundle, &client.base);
+
+ platformInitialize(initializationUserData);
+
+ activateFonts();
+}
+
+void InjectedBundle::didCreatePage(WKBundlePageRef page)
+{
+ m_pages.append(std::make_unique<InjectedBundlePage>(page));
+}
+
+void InjectedBundle::willDestroyPage(WKBundlePageRef page)
+{
+ m_pages.removeFirstMatching([page] (const std::unique_ptr<InjectedBundlePage>& current) {
+ return current->page() == page;
+ });
+}
+
+void InjectedBundle::didInitializePageGroup(WKBundlePageGroupRef pageGroup)
+{
+ m_pageGroup = pageGroup;
+}
+
+InjectedBundlePage* InjectedBundle::page() const
+{
+ // It might be better to have the UI process send over a reference to the main
+ // page instead of just assuming it's the first one.
+ return m_pages[0].get();
+}
+
+void InjectedBundle::resetLocalSettings()
+{
+ setlocale(LC_ALL, "");
+}
+
+void InjectedBundle::didReceiveMessage(WKStringRef messageName, WKTypeRef messageBody)
+{
+ WKRetainPtr<WKStringRef> errorMessageName(AdoptWK, WKStringCreateWithUTF8CString("Error"));
+ WKRetainPtr<WKStringRef> errorMessageBody(AdoptWK, WKStringCreateWithUTF8CString("Unknown"));
+ WKBundlePostMessage(m_bundle, errorMessageName.get(), errorMessageBody.get());
+}
+
+void InjectedBundle::didReceiveMessageToPage(WKBundlePageRef page, WKStringRef messageName, WKTypeRef messageBody)
+{
+ if (WKStringIsEqualToUTF8CString(messageName, "BeginTest")) {
+ ASSERT(messageBody);
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> dumpPixelsKey(AdoptWK, WKStringCreateWithUTF8CString("DumpPixels"));
+ m_dumpPixels = WKBooleanGetValue(static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, dumpPixelsKey.get())));
+
+ WKRetainPtr<WKStringRef> useWaitToDumpWatchdogTimerKey(AdoptWK, WKStringCreateWithUTF8CString("UseWaitToDumpWatchdogTimer"));
+ m_useWaitToDumpWatchdogTimer = WKBooleanGetValue(static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, useWaitToDumpWatchdogTimerKey.get())));
+
+ WKRetainPtr<WKStringRef> timeoutKey(AdoptWK, WKStringCreateWithUTF8CString("Timeout"));
+ m_timeout = (int)WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeoutKey.get())));
+
+ WKRetainPtr<WKStringRef> ackMessageName(AdoptWK, WKStringCreateWithUTF8CString("Ack"));
+ WKRetainPtr<WKStringRef> ackMessageBody(AdoptWK, WKStringCreateWithUTF8CString("BeginTest"));
+ WKBundlePagePostMessage(page, ackMessageName.get(), ackMessageBody.get());
+
+ beginTesting(messageBodyDictionary);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "Reset")) {
+ ASSERT(messageBody);
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> shouldGCKey(AdoptWK, WKStringCreateWithUTF8CString("ShouldGC"));
+ bool shouldGC = WKBooleanGetValue(static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, shouldGCKey.get())));
+
+ if (shouldGC)
+ WKBundleGarbageCollectJavaScriptObjects(m_bundle);
+
+ WKRetainPtr<WKStringRef> allowedHostsKey(AdoptWK, WKStringCreateWithUTF8CString("AllowedHosts"));
+ WKTypeRef allowedHostsValue = WKDictionaryGetItemForKey(messageBodyDictionary, allowedHostsKey.get());
+ if (allowedHostsValue && WKGetTypeID(allowedHostsValue) == WKArrayGetTypeID()) {
+ WKArrayRef allowedHostsArray = static_cast<WKArrayRef>(allowedHostsValue);
+ for (size_t i = 0, size = WKArrayGetSize(allowedHostsArray); i < size; ++i) {
+ WKTypeRef item = WKArrayGetItemAtIndex(allowedHostsArray, i);
+ if (item && WKGetTypeID(item) == WKStringGetTypeID())
+ m_allowedHosts.append(toWTFString(static_cast<WKStringRef>(item)));
+ }
+ }
+
+ m_state = Idle;
+ m_dumpPixels = false;
+
+ resetLocalSettings();
+ m_testRunner->removeAllWebNotificationPermissions();
+
+ InjectedBundle::page()->resetAfterTest();
+
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "CallAddChromeInputFieldCallback")) {
+ m_testRunner->callAddChromeInputFieldCallback();
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "CallRemoveChromeInputFieldCallback")) {
+ m_testRunner->callRemoveChromeInputFieldCallback();
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "CallFocusWebViewCallback")) {
+ m_testRunner->callFocusWebViewCallback();
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "CallSetBackingScaleFactorCallback")) {
+ m_testRunner->callSetBackingScaleFactorCallback();
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "WorkQueueProcessedCallback")) {
+ if (!topLoadingFrame() && !m_testRunner->waitToDump())
+ InjectedBundle::page()->dump();
+ return;
+ }
+
+ WKRetainPtr<WKStringRef> errorMessageName(AdoptWK, WKStringCreateWithUTF8CString("Error"));
+ WKRetainPtr<WKStringRef> errorMessageBody(AdoptWK, WKStringCreateWithUTF8CString("Unknown"));
+ WKBundlePagePostMessage(page, errorMessageName.get(), errorMessageBody.get());
+}
+
+bool InjectedBundle::booleanForKey(WKDictionaryRef dictionary, const char* key)
+{
+ WKRetainPtr<WKStringRef> wkKey(AdoptWK, WKStringCreateWithUTF8CString(key));
+ WKTypeRef value = WKDictionaryGetItemForKey(dictionary, wkKey.get());
+ if (WKGetTypeID(value) != WKBooleanGetTypeID()) {
+ outputText(makeString("Boolean value for key", key, " not found in dictionary\n"));
+ return false;
+ }
+ return WKBooleanGetValue(static_cast<WKBooleanRef>(value));
+}
+
+void InjectedBundle::beginTesting(WKDictionaryRef settings)
+{
+ m_state = Testing;
+
+ m_pixelResult.clear();
+ m_repaintRects.clear();
+
+ m_testRunner = TestRunner::create();
+ m_gcController = GCController::create();
+ m_eventSendingController = EventSendingController::create();
+ m_textInputController = TextInputController::create();
+ m_accessibilityController = AccessibilityController::create();
+
+ WKBundleSetAllowUniversalAccessFromFileURLs(m_bundle, m_pageGroup, true);
+ WKBundleSetJavaScriptCanAccessClipboard(m_bundle, m_pageGroup, true);
+ WKBundleSetPrivateBrowsingEnabled(m_bundle, m_pageGroup, false);
+ WKBundleSetAuthorAndUserStylesEnabled(m_bundle, m_pageGroup, true);
+ WKBundleSetFrameFlatteningEnabled(m_bundle, m_pageGroup, false);
+ WKBundleSetMinimumLogicalFontSize(m_bundle, m_pageGroup, 9);
+ WKBundleSetSpatialNavigationEnabled(m_bundle, m_pageGroup, false);
+ WKBundleSetAllowFileAccessFromFileURLs(m_bundle, m_pageGroup, true);
+ WKBundleSetPluginsEnabled(m_bundle, m_pageGroup, true);
+ WKBundleSetPopupBlockingEnabled(m_bundle, m_pageGroup, false);
+
+ WKBundleRemoveAllUserContent(m_bundle, m_pageGroup);
+
+ m_testRunner->setShouldDumpFrameLoadCallbacks(booleanForKey(settings, "DumpFrameLoadDelegates"));
+ m_testRunner->setUserStyleSheetEnabled(false);
+ m_testRunner->setXSSAuditorEnabled(false);
+ m_testRunner->setCloseRemainingWindowsWhenComplete(false);
+ m_testRunner->setAcceptsEditing(true);
+ m_testRunner->setTabKeyCyclesThroughElements(true);
+
+ if (m_timeout > 0)
+ m_testRunner->setCustomTimeout(m_timeout);
+
+ page()->prepare();
+
+ WKBundleClearAllDatabases(m_bundle);
+ WKBundleClearApplicationCache(m_bundle);
+ WKBundleResetOriginAccessWhitelists(m_bundle);
+
+ // [WK2] REGRESSION(r128623): It made layout tests extremely slow
+ // https://bugs.webkit.org/show_bug.cgi?id=96862
+ // WKBundleSetDatabaseQuota(m_bundle, 5 * 1024 * 1024);
+}
+
+void InjectedBundle::done()
+{
+ m_state = Stopping;
+
+ m_useWorkQueue = false;
+
+ page()->stopLoading();
+ setTopLoadingFrame(0);
+
+ m_testRunner->invalidateWaitToDumpWatchdogTimer();
+
+ m_accessibilityController->resetToConsistentState();
+
+ WKRetainPtr<WKStringRef> doneMessageName(AdoptWK, WKStringCreateWithUTF8CString("Done"));
+ WKRetainPtr<WKMutableDictionaryRef> doneMessageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> pixelResultKey = adoptWK(WKStringCreateWithUTF8CString("PixelResult"));
+ WKDictionarySetItem(doneMessageBody.get(), pixelResultKey.get(), m_pixelResult.get());
+
+ WKRetainPtr<WKStringRef> repaintRectsKey = adoptWK(WKStringCreateWithUTF8CString("RepaintRects"));
+ WKDictionarySetItem(doneMessageBody.get(), repaintRectsKey.get(), m_repaintRects.get());
+
+ WKRetainPtr<WKStringRef> audioResultKey = adoptWK(WKStringCreateWithUTF8CString("AudioResult"));
+ WKDictionarySetItem(doneMessageBody.get(), audioResultKey.get(), m_audioResult.get());
+
+ WKBundlePagePostMessage(page()->page(), doneMessageName.get(), doneMessageBody.get());
+
+ closeOtherPages();
+
+ m_state = Idle;
+}
+
+void InjectedBundle::closeOtherPages()
+{
+ Vector<WKBundlePageRef> pagesToClose;
+ size_t size = m_pages.size();
+ for (size_t i = 1; i < size; ++i)
+ pagesToClose.append(m_pages[i]->page());
+ size = pagesToClose.size();
+ for (size_t i = 0; i < size; ++i)
+ WKBundlePageClose(pagesToClose[i]);
+}
+
+void InjectedBundle::dumpBackForwardListsForAllPages(StringBuilder& stringBuilder)
+{
+ size_t size = m_pages.size();
+ for (size_t i = 0; i < size; ++i)
+ m_pages[i]->dumpBackForwardList(stringBuilder);
+}
+
+void InjectedBundle::outputText(const String& output)
+{
+ if (m_state != Testing)
+ return;
+ if (output.isEmpty())
+ return;
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("TextOutput"));
+ WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithUTF8CString(output.utf8().data()));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::postNewBeforeUnloadReturnValue(bool value)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("BeforeUnloadReturnValue"));
+ WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(value));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::postAddChromeInputField()
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("AddChromeInputField"));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), 0);
+}
+
+void InjectedBundle::postRemoveChromeInputField()
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("RemoveChromeInputField"));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), 0);
+}
+
+void InjectedBundle::postFocusWebView()
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("FocusWebView"));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), 0);
+}
+
+void InjectedBundle::postSetBackingScaleFactor(double backingScaleFactor)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetBackingScaleFactor"));
+ WKRetainPtr<WKDoubleRef> messageBody(AdoptWK, WKDoubleCreate(backingScaleFactor));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::postSetWindowIsKey(bool isKey)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetWindowIsKey"));
+ WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(isKey));
+ WKBundlePagePostSynchronousMessage(page()->page(), messageName.get(), messageBody.get(), 0);
+}
+
+void InjectedBundle::postSimulateWebNotificationClick(uint64_t notificationID)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SimulateWebNotificationClick"));
+ WKRetainPtr<WKUInt64Ref> messageBody(AdoptWK, WKUInt64Create(notificationID));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::postSetAddsVisitedLinks(bool addsVisitedLinks)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAddsVisitedLinks"));
+ WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(addsVisitedLinks));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::setGeolocationPermission(bool enabled)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetGeolocationPermission"));
+ WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(enabled));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::setMockGeolocationPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetMockGeolocationPosition"));
+
+ WKRetainPtr<WKMutableDictionaryRef> messageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> latitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("latitude"));
+ WKRetainPtr<WKDoubleRef> latitudeWK(AdoptWK, WKDoubleCreate(latitude));
+ WKDictionarySetItem(messageBody.get(), latitudeKeyWK.get(), latitudeWK.get());
+
+ WKRetainPtr<WKStringRef> longitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("longitude"));
+ WKRetainPtr<WKDoubleRef> longitudeWK(AdoptWK, WKDoubleCreate(longitude));
+ WKDictionarySetItem(messageBody.get(), longitudeKeyWK.get(), longitudeWK.get());
+
+ WKRetainPtr<WKStringRef> accuracyKeyWK(AdoptWK, WKStringCreateWithUTF8CString("accuracy"));
+ WKRetainPtr<WKDoubleRef> accuracyWK(AdoptWK, WKDoubleCreate(accuracy));
+ WKDictionarySetItem(messageBody.get(), accuracyKeyWK.get(), accuracyWK.get());
+
+ WKRetainPtr<WKStringRef> providesAltitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesAltitude"));
+ WKRetainPtr<WKBooleanRef> providesAltitudeWK(AdoptWK, WKBooleanCreate(providesAltitude));
+ WKDictionarySetItem(messageBody.get(), providesAltitudeKeyWK.get(), providesAltitudeWK.get());
+
+ WKRetainPtr<WKStringRef> altitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("altitude"));
+ WKRetainPtr<WKDoubleRef> altitudeWK(AdoptWK, WKDoubleCreate(altitude));
+ WKDictionarySetItem(messageBody.get(), altitudeKeyWK.get(), altitudeWK.get());
+
+ WKRetainPtr<WKStringRef> providesAltitudeAccuracyKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesAltitudeAccuracy"));
+ WKRetainPtr<WKBooleanRef> providesAltitudeAccuracyWK(AdoptWK, WKBooleanCreate(providesAltitudeAccuracy));
+ WKDictionarySetItem(messageBody.get(), providesAltitudeAccuracyKeyWK.get(), providesAltitudeAccuracyWK.get());
+
+ WKRetainPtr<WKStringRef> altitudeAccuracyKeyWK(AdoptWK, WKStringCreateWithUTF8CString("altitudeAccuracy"));
+ WKRetainPtr<WKDoubleRef> altitudeAccuracyWK(AdoptWK, WKDoubleCreate(altitudeAccuracy));
+ WKDictionarySetItem(messageBody.get(), altitudeAccuracyKeyWK.get(), altitudeAccuracyWK.get());
+
+ WKRetainPtr<WKStringRef> providesHeadingKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesHeading"));
+ WKRetainPtr<WKBooleanRef> providesHeadingWK(AdoptWK, WKBooleanCreate(providesHeading));
+ WKDictionarySetItem(messageBody.get(), providesHeadingKeyWK.get(), providesHeadingWK.get());
+
+ WKRetainPtr<WKStringRef> headingKeyWK(AdoptWK, WKStringCreateWithUTF8CString("heading"));
+ WKRetainPtr<WKDoubleRef> headingWK(AdoptWK, WKDoubleCreate(heading));
+ WKDictionarySetItem(messageBody.get(), headingKeyWK.get(), headingWK.get());
+
+ WKRetainPtr<WKStringRef> providesSpeedKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesSpeed"));
+ WKRetainPtr<WKBooleanRef> providesSpeedWK(AdoptWK, WKBooleanCreate(providesSpeed));
+ WKDictionarySetItem(messageBody.get(), providesSpeedKeyWK.get(), providesSpeedWK.get());
+
+ WKRetainPtr<WKStringRef> speedKeyWK(AdoptWK, WKStringCreateWithUTF8CString("speed"));
+ WKRetainPtr<WKDoubleRef> speedWK(AdoptWK, WKDoubleCreate(speed));
+ WKDictionarySetItem(messageBody.get(), speedKeyWK.get(), speedWK.get());
+
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetMockGeolocationPositionUnavailableError"));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), errorMessage);
+}
+
+bool InjectedBundle::isGeolocationProviderActive() const
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("IsGeolocationClientActive"));
+ WKTypeRef resultToPass = 0;
+ WKBundlePagePostSynchronousMessage(page()->page(), messageName.get(), 0, &resultToPass);
+ WKRetainPtr<WKBooleanRef> isActive(AdoptWK, static_cast<WKBooleanRef>(resultToPass));
+
+ return WKBooleanGetValue(isActive.get());
+}
+
+void InjectedBundle::setUserMediaPermission(bool enabled)
+{
+ auto messageName = adoptWK(WKStringCreateWithUTF8CString("SetUserMediaPermission"));
+ auto messageBody = adoptWK(WKBooleanCreate(enabled));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::setCustomPolicyDelegate(bool enabled, bool permissive)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetCustomPolicyDelegate"));
+
+ WKRetainPtr<WKMutableDictionaryRef> messageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> enabledKeyWK(AdoptWK, WKStringCreateWithUTF8CString("enabled"));
+ WKRetainPtr<WKBooleanRef> enabledWK(AdoptWK, WKBooleanCreate(enabled));
+ WKDictionarySetItem(messageBody.get(), enabledKeyWK.get(), enabledWK.get());
+
+ WKRetainPtr<WKStringRef> permissiveKeyWK(AdoptWK, WKStringCreateWithUTF8CString("permissive"));
+ WKRetainPtr<WKBooleanRef> permissiveWK(AdoptWK, WKBooleanCreate(permissive));
+ WKDictionarySetItem(messageBody.get(), permissiveKeyWK.get(), permissiveWK.get());
+
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::setHidden(bool hidden)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetHidden"));
+ WKRetainPtr<WKMutableDictionaryRef> messageBody(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> isInitialKeyWK(AdoptWK, WKStringCreateWithUTF8CString("hidden"));
+ WKRetainPtr<WKBooleanRef> isInitialWK(AdoptWK, WKBooleanCreate(hidden));
+ WKDictionarySetItem(messageBody.get(), isInitialKeyWK.get(), isInitialWK.get());
+
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::setCacheModel(int model)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetCacheModel"));
+ WKRetainPtr<WKUInt64Ref> messageBody(AdoptWK, WKUInt64Create(model));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+bool InjectedBundle::shouldProcessWorkQueue() const
+{
+ if (!m_useWorkQueue)
+ return false;
+
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("IsWorkQueueEmpty"));
+ WKTypeRef resultToPass = 0;
+ WKBundlePagePostSynchronousMessage(page()->page(), messageName.get(), 0, &resultToPass);
+ WKRetainPtr<WKBooleanRef> isEmpty(AdoptWK, static_cast<WKBooleanRef>(resultToPass));
+
+ return !WKBooleanGetValue(isEmpty.get());
+}
+
+void InjectedBundle::processWorkQueue()
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("ProcessWorkQueue"));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), 0);
+}
+
+void InjectedBundle::queueBackNavigation(unsigned howFarBackward)
+{
+ m_useWorkQueue = true;
+
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("QueueBackNavigation"));
+ WKRetainPtr<WKUInt64Ref> messageBody(AdoptWK, WKUInt64Create(howFarBackward));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::queueForwardNavigation(unsigned howFarForward)
+{
+ m_useWorkQueue = true;
+
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("QueueForwardNavigation"));
+ WKRetainPtr<WKUInt64Ref> messageBody(AdoptWK, WKUInt64Create(howFarForward));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), messageBody.get());
+}
+
+void InjectedBundle::queueLoad(WKStringRef url, WKStringRef target, bool shouldOpenExternalURLs)
+{
+ m_useWorkQueue = true;
+
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("QueueLoad"));
+
+ WKRetainPtr<WKMutableDictionaryRef> loadData(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> urlKey(AdoptWK, WKStringCreateWithUTF8CString("url"));
+ WKDictionarySetItem(loadData.get(), urlKey.get(), url);
+
+ WKRetainPtr<WKStringRef> targetKey(AdoptWK, WKStringCreateWithUTF8CString("target"));
+ WKDictionarySetItem(loadData.get(), targetKey.get(), target);
+
+ WKRetainPtr<WKStringRef> shouldOpenExternalURLsKey(AdoptWK, WKStringCreateWithUTF8CString("shouldOpenExternalURLs"));
+ WKRetainPtr<WKBooleanRef> shouldOpenExternalURLsValue(AdoptWK, WKBooleanCreate(shouldOpenExternalURLs));
+ WKDictionarySetItem(loadData.get(), shouldOpenExternalURLsKey.get(), shouldOpenExternalURLsValue.get());
+
+ WKBundlePagePostMessage(page()->page(), messageName.get(), loadData.get());
+}
+
+void InjectedBundle::queueLoadHTMLString(WKStringRef content, WKStringRef baseURL, WKStringRef unreachableURL)
+{
+ m_useWorkQueue = true;
+
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("QueueLoadHTMLString"));
+
+ WKRetainPtr<WKMutableDictionaryRef> loadData(AdoptWK, WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> contentKey(AdoptWK, WKStringCreateWithUTF8CString("content"));
+ WKDictionarySetItem(loadData.get(), contentKey.get(), content);
+
+ if (baseURL) {
+ WKRetainPtr<WKStringRef> baseURLKey(AdoptWK, WKStringCreateWithUTF8CString("baseURL"));
+ WKDictionarySetItem(loadData.get(), baseURLKey.get(), baseURL);
+ }
+
+ if (unreachableURL) {
+ WKRetainPtr<WKStringRef> unreachableURLKey(AdoptWK, WKStringCreateWithUTF8CString("unreachableURL"));
+ WKDictionarySetItem(loadData.get(), unreachableURLKey.get(), unreachableURL);
+ }
+
+ WKBundlePagePostMessage(page()->page(), messageName.get(), loadData.get());
+}
+
+void InjectedBundle::queueReload()
+{
+ m_useWorkQueue = true;
+
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("QueueReload"));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), 0);
+}
+
+void InjectedBundle::queueLoadingScript(WKStringRef script)
+{
+ m_useWorkQueue = true;
+
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("QueueLoadingScript"));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), script);
+}
+
+void InjectedBundle::queueNonLoadingScript(WKStringRef script)
+{
+ m_useWorkQueue = true;
+
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("QueueNonLoadingScript"));
+ WKBundlePagePostMessage(page()->page(), messageName.get(), script);
+}
+
+bool InjectedBundle::isAllowedHost(WKStringRef host)
+{
+ if (m_allowedHosts.isEmpty())
+ return false;
+ return m_allowedHosts.contains(toWTFString(host));
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h
new file mode 100644
index 000000000..a23f64a87
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2010, 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef InjectedBundle_h
+#define InjectedBundle_h
+
+#include "AccessibilityController.h"
+#include "EventSendingController.h"
+#include "GCController.h"
+#include "TestRunner.h"
+#include "TextInputController.h"
+#include <WebKit/WKBase.h>
+#include <WebKit/WKRetainPtr.h>
+#include <sstream>
+#include <wtf/Forward.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WTR {
+
+class InjectedBundlePage;
+
+class InjectedBundle {
+public:
+ static InjectedBundle& singleton();
+
+ // Initialize the InjectedBundle.
+ void initialize(WKBundleRef, WKTypeRef initializationUserData);
+
+ WKBundleRef bundle() const { return m_bundle; }
+ WKBundlePageGroupRef pageGroup() const { return m_pageGroup; }
+
+ TestRunner* testRunner() { return m_testRunner.get(); }
+ GCController* gcController() { return m_gcController.get(); }
+ EventSendingController* eventSendingController() { return m_eventSendingController.get(); }
+ TextInputController* textInputController() { return m_textInputController.get(); }
+ AccessibilityController* accessibilityController() { return m_accessibilityController.get(); }
+
+ InjectedBundlePage* page() const;
+ size_t pageCount() const { return m_pages.size(); }
+ void closeOtherPages();
+
+ void dumpBackForwardListsForAllPages(StringBuilder&);
+
+ void done();
+ void setAudioResult(WKDataRef audioData) { m_audioResult = audioData; }
+ void setPixelResult(WKImageRef image) { m_pixelResult = image; }
+ void setRepaintRects(WKArrayRef rects) { m_repaintRects = rects; }
+
+ bool isTestRunning() { return m_state == Testing; }
+
+ WKBundleFrameRef topLoadingFrame() { return m_topLoadingFrame; }
+ void setTopLoadingFrame(WKBundleFrameRef frame) { m_topLoadingFrame = frame; }
+
+ bool shouldDumpPixels() const { return m_dumpPixels; }
+ bool useWaitToDumpWatchdogTimer() const { return m_useWaitToDumpWatchdogTimer; }
+
+ void outputText(const String&);
+ void postNewBeforeUnloadReturnValue(bool);
+ void postAddChromeInputField();
+ void postRemoveChromeInputField();
+ void postFocusWebView();
+ void postSetBackingScaleFactor(double);
+ void postSetWindowIsKey(bool);
+ void postSimulateWebNotificationClick(uint64_t notificationID);
+ void postSetAddsVisitedLinks(bool);
+
+ // Geolocation.
+ void setGeolocationPermission(bool);
+ void setMockGeolocationPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed);
+ void setMockGeolocationPositionUnavailableError(WKStringRef errorMessage);
+ bool isGeolocationProviderActive() const;
+
+ // MediaStream.
+ void setUserMediaPermission(bool);
+
+ // Policy delegate.
+ void setCustomPolicyDelegate(bool enabled, bool permissive);
+
+ // Page Visibility.
+ void setHidden(bool);
+
+ // Cache.
+ void setCacheModel(int);
+
+ // Work queue.
+ bool shouldProcessWorkQueue() const;
+ void processWorkQueue();
+ void queueBackNavigation(unsigned howFarBackward);
+ void queueForwardNavigation(unsigned howFarForward);
+ void queueLoad(WKStringRef url, WKStringRef target, bool shouldOpenExternalURLs = false);
+ void queueLoadHTMLString(WKStringRef content, WKStringRef baseURL = 0, WKStringRef unreachableURL = 0);
+ void queueReload();
+ void queueLoadingScript(WKStringRef script);
+ void queueNonLoadingScript(WKStringRef script);
+
+ bool isAllowedHost(WKStringRef);
+
+private:
+ InjectedBundle();
+ ~InjectedBundle();
+
+ static void didCreatePage(WKBundleRef, WKBundlePageRef, const void* clientInfo);
+ static void willDestroyPage(WKBundleRef, WKBundlePageRef, const void* clientInfo);
+ static void didInitializePageGroup(WKBundleRef, WKBundlePageGroupRef, const void* clientInfo);
+ static void didReceiveMessage(WKBundleRef, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo);
+ static void didReceiveMessageToPage(WKBundleRef, WKBundlePageRef, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo);
+
+ void didCreatePage(WKBundlePageRef);
+ void willDestroyPage(WKBundlePageRef);
+ void didInitializePageGroup(WKBundlePageGroupRef);
+ void didReceiveMessage(WKStringRef messageName, WKTypeRef messageBody);
+ void didReceiveMessageToPage(WKBundlePageRef, WKStringRef messageName, WKTypeRef messageBody);
+
+ void platformInitialize(WKTypeRef initializationUserData);
+ void resetLocalSettings();
+
+ void beginTesting(WKDictionaryRef initialSettings);
+
+ bool booleanForKey(WKDictionaryRef, const char* key);
+
+ WKBundleRef m_bundle;
+ WKBundlePageGroupRef m_pageGroup;
+ Vector<std::unique_ptr<InjectedBundlePage>> m_pages;
+
+ RefPtr<AccessibilityController> m_accessibilityController;
+ RefPtr<TestRunner> m_testRunner;
+ RefPtr<GCController> m_gcController;
+ RefPtr<EventSendingController> m_eventSendingController;
+ RefPtr<TextInputController> m_textInputController;
+
+ WKBundleFrameRef m_topLoadingFrame;
+
+ enum State {
+ Idle,
+ Testing,
+ Stopping
+ };
+ State m_state;
+
+ bool m_dumpPixels;
+ bool m_useWaitToDumpWatchdogTimer;
+ bool m_useWorkQueue;
+ int m_timeout;
+
+ WKRetainPtr<WKDataRef> m_audioResult;
+ WKRetainPtr<WKImageRef> m_pixelResult;
+ WKRetainPtr<WKArrayRef> m_repaintRects;
+
+ Vector<String> m_allowedHosts;
+};
+
+} // namespace WTR
+
+#endif // InjectedBundle_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundleMain.cpp b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundleMain.cpp
new file mode 100644
index 000000000..4f1272214
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundleMain.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "InjectedBundle.h"
+#include <WebKit/WKBundleInitialize.h>
+
+extern "C"
+void WKBundleInitialize(WKBundleRef bundle, WKTypeRef initializationUserData)
+{
+ WTR::InjectedBundle::singleton().initialize(bundle, initializationUserData);
+}
diff --git a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp
new file mode 100644
index 000000000..9d232ee16
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp
@@ -0,0 +1,1993 @@
+/*
+ * Copyright (C) 2010, 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InjectedBundlePage.h"
+
+#include "InjectedBundle.h"
+#include "StringFunctions.h"
+#include "WebCoreTestSupport.h"
+#include <cmath>
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <WebKit/WKArray.h>
+#include <WebKit/WKBundle.h>
+#include <WebKit/WKBundleBackForwardList.h>
+#include <WebKit/WKBundleBackForwardListItem.h>
+#include <WebKit/WKBundleFrame.h>
+#include <WebKit/WKBundleFramePrivate.h>
+#include <WebKit/WKBundleHitTestResult.h>
+#include <WebKit/WKBundleNavigationAction.h>
+#include <WebKit/WKBundleNavigationActionPrivate.h>
+#include <WebKit/WKBundleNodeHandlePrivate.h>
+#include <WebKit/WKBundlePagePrivate.h>
+#include <WebKit/WKBundlePrivate.h>
+#include <WebKit/WKSecurityOriginRef.h>
+#include <WebKit/WKURLRequest.h>
+#include <wtf/HashMap.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+
+#if USE(CF)
+#include "WebArchiveDumpSupport.h"
+#endif
+
+using namespace std;
+
+namespace WTR {
+
+static bool hasPrefix(const WTF::String& searchString, const WTF::String& prefix)
+{
+ return searchString.length() >= prefix.length() && searchString.substring(0, prefix.length()) == prefix;
+}
+
+static JSValueRef propertyValue(JSContextRef context, JSObjectRef object, const char* propertyName)
+{
+ if (!object)
+ return 0;
+ JSRetainPtr<JSStringRef> propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName));
+ return JSObjectGetProperty(context, object, propertyNameString.get(), 0);
+}
+
+static double propertyValueDouble(JSContextRef context, JSObjectRef object, const char* propertyName)
+{
+ JSValueRef value = propertyValue(context, object, propertyName);
+ if (!value)
+ return 0;
+ return JSValueToNumber(context, value, 0);
+}
+
+static int propertyValueInt(JSContextRef context, JSObjectRef object, const char* propertyName)
+{
+ return static_cast<int>(propertyValueDouble(context, object, propertyName));
+}
+
+static double numericWindowPropertyValue(WKBundleFrameRef frame, const char* propertyName)
+{
+ JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
+ return propertyValueDouble(context, JSContextGetGlobalObject(context), propertyName);
+}
+
+static WTF::String dumpPath(JSGlobalContextRef context, JSObjectRef nodeValue)
+{
+ JSValueRef nodeNameValue = propertyValue(context, nodeValue, "nodeName");
+ JSRetainPtr<JSStringRef> jsStringNodeName(Adopt, JSValueToStringCopy(context, nodeNameValue, 0));
+ WKRetainPtr<WKStringRef> nodeName = toWK(jsStringNodeName);
+
+ JSValueRef parentNode = propertyValue(context, nodeValue, "parentNode");
+
+ StringBuilder stringBuilder;
+ stringBuilder.append(toWTFString(nodeName));
+
+ if (parentNode && JSValueIsObject(context, parentNode)) {
+ stringBuilder.appendLiteral(" > ");
+ stringBuilder.append(dumpPath(context, (JSObjectRef)parentNode));
+ }
+
+ return stringBuilder.toString();
+}
+
+static WTF::String dumpPath(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleNodeHandleRef node)
+{
+ if (!node)
+ return "(null)";
+
+ WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
+
+ JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
+ JSValueRef nodeValue = WKBundleFrameGetJavaScriptWrapperForNodeForWorld(frame, node, world);
+ ASSERT(JSValueIsObject(context, nodeValue));
+ JSObjectRef nodeObject = (JSObjectRef)nodeValue;
+
+ return dumpPath(context, nodeObject);
+}
+
+static WTF::String rangeToStr(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleRangeHandleRef rangeRef)
+{
+ if (!rangeRef)
+ return "(null)";
+
+ WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
+
+ JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
+ JSValueRef rangeValue = WKBundleFrameGetJavaScriptWrapperForRangeForWorld(frame, rangeRef, world);
+ ASSERT(JSValueIsObject(context, rangeValue));
+ JSObjectRef rangeObject = (JSObjectRef)rangeValue;
+
+ JSValueRef startNodeValue = propertyValue(context, rangeObject, "startContainer");
+ ASSERT(JSValueIsObject(context, startNodeValue));
+ JSObjectRef startNodeObject = (JSObjectRef)startNodeValue;
+
+ JSValueRef endNodeValue = propertyValue(context, rangeObject, "endContainer");
+ ASSERT(JSValueIsObject(context, endNodeValue));
+ JSObjectRef endNodeObject = (JSObjectRef)endNodeValue;
+
+ int startOffset = propertyValueInt(context, rangeObject, "startOffset");
+ int endOffset = propertyValueInt(context, rangeObject, "endOffset");
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("range from ");
+ stringBuilder.appendNumber(startOffset);
+ stringBuilder.appendLiteral(" of ");
+ stringBuilder.append(dumpPath(context, startNodeObject));
+ stringBuilder.appendLiteral(" to ");
+ stringBuilder.appendNumber(endOffset);
+ stringBuilder.appendLiteral(" of ");
+ stringBuilder.append(dumpPath(context, endNodeObject));
+ return stringBuilder.toString();
+}
+
+static WKRetainPtr<WKStringRef> NavigationTypeToString(WKFrameNavigationType type)
+{
+ switch (type) {
+ case kWKFrameNavigationTypeLinkClicked:
+ return adoptWK(WKStringCreateWithUTF8CString("link clicked"));
+ case kWKFrameNavigationTypeFormSubmitted:
+ return adoptWK(WKStringCreateWithUTF8CString("form submitted"));
+ case kWKFrameNavigationTypeBackForward:
+ return adoptWK(WKStringCreateWithUTF8CString("back/forward"));
+ case kWKFrameNavigationTypeReload:
+ return adoptWK(WKStringCreateWithUTF8CString("reload"));
+ case kWKFrameNavigationTypeFormResubmitted:
+ return adoptWK(WKStringCreateWithUTF8CString("form resubmitted"));
+ case kWKFrameNavigationTypeOther:
+ return adoptWK(WKStringCreateWithUTF8CString("other"));
+ }
+ return adoptWK(WKStringCreateWithUTF8CString("illegal value"));
+}
+
+static WTF::String styleDecToStr(WKBundleCSSStyleDeclarationRef style)
+{
+ // DumpRenderTree calls -[DOMCSSStyleDeclaration description], which just dumps class name and object address.
+ // No existing tests actually hit this code path at the time of this writing, because WebCore doesn't call
+ // the editing client if the styling operation source is CommandFromDOM or CommandFromDOMWithUserInterface.
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("<DOMCSSStyleDeclaration ADDRESS>");
+ return stringBuilder.toString();
+}
+
+static WTF::String securityOriginToStr(WKSecurityOriginRef origin)
+{
+ StringBuilder stringBuilder;
+ stringBuilder.append('{');
+ stringBuilder.append(toWTFString(adoptWK(WKSecurityOriginCopyProtocol(origin))));
+ stringBuilder.appendLiteral(", ");
+ stringBuilder.append(toWTFString(adoptWK(WKSecurityOriginCopyHost(origin))));
+ stringBuilder.appendLiteral(", ");
+ stringBuilder.appendNumber(WKSecurityOriginGetPort(origin));
+ stringBuilder.append('}');
+
+ return stringBuilder.toString();
+}
+
+static WTF::String frameToStr(WKBundleFrameRef frame)
+{
+ WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
+ StringBuilder stringBuilder;
+ if (WKBundleFrameIsMainFrame(frame)) {
+ if (!WKStringIsEmpty(name.get())) {
+ stringBuilder.appendLiteral("main frame \"");
+ stringBuilder.append(toWTFString(name));
+ stringBuilder.append('"');
+ } else
+ stringBuilder.appendLiteral("main frame");
+ } else {
+ if (!WKStringIsEmpty(name.get())) {
+ stringBuilder.appendLiteral("frame \"");
+ stringBuilder.append(toWTFString(name));
+ stringBuilder.append('"');
+ }
+ else
+ stringBuilder.appendLiteral("frame (anonymous)");
+ }
+
+ return stringBuilder.toString();
+}
+
+static inline bool isLocalFileScheme(WKStringRef scheme)
+{
+ return WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "file");
+}
+
+static const char divider = '/';
+
+static inline WTF::String pathSuitableForTestResult(WKURLRef fileUrl)
+{
+ if (!fileUrl)
+ return "(null)";
+
+ WKRetainPtr<WKStringRef> schemeString = adoptWK(WKURLCopyScheme(fileUrl));
+ if (!isLocalFileScheme(schemeString.get()))
+ return toWTFString(adoptWK(WKURLCopyString(fileUrl)));
+
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
+ WKRetainPtr<WKURLRef> mainFrameURL = adoptWK(WKBundleFrameCopyURL(mainFrame));
+ if (!mainFrameURL)
+ mainFrameURL = adoptWK(WKBundleFrameCopyProvisionalURL(mainFrame));
+
+ String pathString = toWTFString(adoptWK(WKURLCopyPath(fileUrl)));
+ String mainFrameURLPathString = toWTFString(adoptWK(WKURLCopyPath(mainFrameURL.get())));
+ String basePath = mainFrameURLPathString.substring(0, mainFrameURLPathString.reverseFind(divider) + 1);
+
+ if (!basePath.isEmpty() && pathString.startsWith(basePath))
+ return pathString.substring(basePath.length());
+ return toWTFString(adoptWK(WKURLCopyLastPathComponent(fileUrl))); // We lose some information here, but it's better than exposing a full path, which is always machine specific.
+}
+
+static HashMap<uint64_t, String> assignedUrlsCache;
+
+static inline void dumpResourceURL(uint64_t identifier, StringBuilder& stringBuilder)
+{
+ if (assignedUrlsCache.contains(identifier))
+ stringBuilder.append(assignedUrlsCache.get(identifier));
+ else
+ stringBuilder.appendLiteral("<unknown>");
+}
+
+InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page)
+ : m_page(page)
+ , m_world(AdoptWK, WKBundleScriptWorldCreateWorld())
+{
+ WKBundlePageLoaderClientV8 loaderClient = {
+ { 8, this },
+ didStartProvisionalLoadForFrame,
+ didReceiveServerRedirectForProvisionalLoadForFrame,
+ didFailProvisionalLoadWithErrorForFrame,
+ didCommitLoadForFrame,
+ didFinishDocumentLoadForFrame,
+ didFinishLoadForFrame,
+ didFailLoadWithErrorForFrame,
+ didSameDocumentNavigationForFrame,
+ didReceiveTitleForFrame,
+ 0, // didFirstLayoutForFrame
+ 0, // didFirstVisuallyNonEmptyLayoutForFrame
+ 0, // didRemoveFrameFromHierarchy
+ didDisplayInsecureContentForFrame,
+ didRunInsecureContentForFrame,
+ didClearWindowForFrame,
+ didCancelClientRedirectForFrame,
+ willPerformClientRedirectForFrame,
+ didHandleOnloadEventsForFrame,
+ 0, // didLayoutForFrame
+ 0, // didNewFirstVisuallyNonEmptyLayout_unavailable
+ didDetectXSSForFrame,
+ 0, // shouldGoToBackForwardListItem
+ 0, // didCreateGlobalObjectForFrame
+ 0, // willDisconnectDOMWindowExtensionFromGlobalObject
+ 0, // didReconnectDOMWindowExtensionToGlobalObject
+ 0, // willDestroyGlobalObjectForDOMWindowExtension
+ didFinishProgress, // didFinishProgress
+ 0, // shouldForceUniversalAccessFromLocalURL
+ 0, // didReceiveIntentForFrame
+ 0, // registerIntentServiceForFrame
+ 0, // didLayout
+ 0, // featuresUsedInPage
+ 0, // willLoadURLRequest
+ 0, // willLoadDataRequest
+ 0, // willDestroyFrame_unavailable
+ 0, // userAgentForURL
+ };
+ WKBundlePageSetPageLoaderClient(m_page, &loaderClient.base);
+
+ WKBundlePageResourceLoadClientV1 resourceLoadClient = {
+ { 1, this },
+ didInitiateLoadForResource,
+ willSendRequestForFrame,
+ didReceiveResponseForResource,
+ didReceiveContentLengthForResource,
+ didFinishLoadForResource,
+ didFailLoadForResource,
+ shouldCacheResponse,
+ 0 // shouldUseCredentialStorage
+ };
+ WKBundlePageSetResourceLoadClient(m_page, &resourceLoadClient.base);
+
+ WKBundlePagePolicyClientV0 policyClient = {
+ { 0, this },
+ decidePolicyForNavigationAction,
+ decidePolicyForNewWindowAction,
+ decidePolicyForResponse,
+ unableToImplementPolicy
+ };
+ WKBundlePageSetPolicyClient(m_page, &policyClient.base);
+
+ WKBundlePageUIClientV2 uiClient = {
+ { 2, this },
+ willAddMessageToConsole,
+ willSetStatusbarText,
+ willRunJavaScriptAlert,
+ willRunJavaScriptConfirm,
+ willRunJavaScriptPrompt,
+ 0, /*mouseDidMoveOverElement*/
+ 0, /*pageDidScroll*/
+ 0, /*paintCustomOverhangArea*/
+ 0, /*shouldGenerateFileForUpload*/
+ 0, /*generateFileForUpload*/
+ 0, /*shouldRubberBandInDirection*/
+ 0, /*statusBarIsVisible*/
+ 0, /*menuBarIsVisible*/
+ 0, /*toolbarsAreVisible*/
+ didReachApplicationCacheOriginQuota,
+ didExceedDatabaseQuota,
+ 0, /*plugInStartLabelTitle*/
+ 0, /*plugInStartLabelSubtitle*/
+ 0, /*plugInExtraStyleSheet*/
+ 0, /*plugInExtraScript*/
+ };
+ WKBundlePageSetUIClient(m_page, &uiClient.base);
+
+ WKBundlePageEditorClientV1 editorClient = {
+ { 1, this },
+ shouldBeginEditing,
+ shouldEndEditing,
+ shouldInsertNode,
+ shouldInsertText,
+ shouldDeleteRange,
+ shouldChangeSelectedRange,
+ shouldApplyStyle,
+ didBeginEditing,
+ didEndEditing,
+ didChange,
+ didChangeSelection,
+ 0, /* willWriteToPasteboard */
+ 0, /* getPasteboardDataForRange */
+ 0 /* didWriteToPasteboard */
+ };
+ WKBundlePageSetEditorClient(m_page, &editorClient.base);
+
+#if ENABLE(FULLSCREEN_API)
+ WKBundlePageFullScreenClientV1 fullScreenClient = {
+ { 1, this },
+ supportsFullScreen,
+ enterFullScreenForElement,
+ exitFullScreenForElement,
+ beganEnterFullScreen,
+ beganExitFullScreen,
+ closeFullScreen,
+ };
+ WKBundlePageSetFullScreenClient(m_page, &fullScreenClient.base);
+#endif
+}
+
+InjectedBundlePage::~InjectedBundlePage()
+{
+}
+
+void InjectedBundlePage::stopLoading()
+{
+ WKBundlePageStopLoading(m_page);
+}
+
+void InjectedBundlePage::prepare()
+{
+ WKBundlePageClearMainFrameName(m_page);
+
+ WKBundlePageSetPageZoomFactor(m_page, 1);
+ WKBundlePageSetTextZoomFactor(m_page, 1);
+
+ WKPoint origin = { 0, 0 };
+ WKBundlePageSetScaleAtOrigin(m_page, 1, origin);
+
+ m_previousTestBackForwardListItem = adoptWK(WKBundleBackForwardListCopyItemAtIndex(WKBundlePageGetBackForwardList(m_page), 0));
+
+ WKBundleFrameClearOpener(WKBundlePageGetMainFrame(m_page));
+
+ WKBundlePageSetTracksRepaints(m_page, false);
+}
+
+void InjectedBundlePage::resetAfterTest()
+{
+ WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
+
+ // WebKit currently doesn't reset focus even when navigating to a new page. This may or may not be a bug
+ // (see <https://bugs.webkit.org/show_bug.cgi?id=138334>), however for tests, we want to start each one with a clean state.
+ WKBundleFrameFocus(frame);
+
+ JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
+ WebCoreTestSupport::resetInternalsObject(context);
+ assignedUrlsCache.clear();
+}
+
+// Loader Client Callbacks
+
+// String output must be identical to -[WebFrame _drt_descriptionSuitableForTestResult].
+static void dumpFrameDescriptionSuitableForTestResult(WKBundleFrameRef frame, StringBuilder& stringBuilder)
+{
+ WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
+ if (WKBundleFrameIsMainFrame(frame)) {
+ if (WKStringIsEmpty(name.get())) {
+ stringBuilder.appendLiteral("main frame");
+ return;
+ }
+
+ stringBuilder.appendLiteral("main frame \"");
+ stringBuilder.append(toWTFString(name));
+ stringBuilder.append('"');
+ return;
+ }
+
+ if (WKStringIsEmpty(name.get())) {
+ stringBuilder.appendLiteral("frame (anonymous)");
+ return;
+ }
+
+ stringBuilder.appendLiteral("frame \"");
+ stringBuilder.append(toWTFString(name));
+ stringBuilder.append('"');
+}
+
+static void dumpLoadEvent(WKBundleFrameRef frame, const char* eventName)
+{
+ StringBuilder stringBuilder;
+ dumpFrameDescriptionSuitableForTestResult(frame, stringBuilder);
+ stringBuilder.appendLiteral(" - ");
+ stringBuilder.append(eventName);
+ stringBuilder.append('\n');
+ InjectedBundle::singleton().outputText(stringBuilder.toString());
+}
+
+static inline void dumpRequestDescriptionSuitableForTestResult(WKURLRequestRef request, StringBuilder& stringBuilder)
+{
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
+ WKRetainPtr<WKURLRef> firstParty = adoptWK(WKURLRequestCopyFirstPartyForCookies(request));
+ WKRetainPtr<WKStringRef> httpMethod = adoptWK(WKURLRequestCopyHTTPMethod(request));
+
+ stringBuilder.appendLiteral("<NSURLRequest URL ");
+ stringBuilder.append(pathSuitableForTestResult(url.get()));
+ stringBuilder.appendLiteral(", main document URL ");
+ stringBuilder.append(pathSuitableForTestResult(firstParty.get()));
+ stringBuilder.appendLiteral(", http method ");
+
+ if (WKStringIsEmpty(httpMethod.get()))
+ stringBuilder.appendLiteral("(none)");
+ else
+ stringBuilder.append(toWTFString(httpMethod));
+
+ stringBuilder.append('>');
+}
+
+static inline void dumpResponseDescriptionSuitableForTestResult(WKURLResponseRef response, StringBuilder& stringBuilder)
+{
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLResponseCopyURL(response));
+ if (!url) {
+ stringBuilder.appendLiteral("(null)");
+ return;
+ }
+ stringBuilder.appendLiteral("<NSURLResponse ");
+ stringBuilder.append(pathSuitableForTestResult(url.get()));
+ stringBuilder.appendLiteral(", http status code ");
+ stringBuilder.appendNumber(WKURLResponseHTTPStatusCode(response));
+ stringBuilder.append('>');
+}
+
+static inline void dumpErrorDescriptionSuitableForTestResult(WKErrorRef error, StringBuilder& stringBuilder)
+{
+ WKRetainPtr<WKStringRef> errorDomain = adoptWK(WKErrorCopyDomain(error));
+ int errorCode = WKErrorGetErrorCode(error);
+
+ // We need to do some error mapping here to match the test expectations (Mac error names are expected).
+ if (WKStringIsEqualToUTF8CString(errorDomain.get(), "WebKitNetworkError")) {
+ errorDomain = adoptWK(WKStringCreateWithUTF8CString("NSURLErrorDomain"));
+ errorCode = -999;
+ }
+
+ if (WKStringIsEqualToUTF8CString(errorDomain.get(), "WebKitPolicyError"))
+ errorDomain = adoptWK(WKStringCreateWithUTF8CString("WebKitErrorDomain"));
+
+ stringBuilder.appendLiteral("<NSError domain ");
+ stringBuilder.append(toWTFString(errorDomain));
+ stringBuilder.appendLiteral(", code ");
+ stringBuilder.appendNumber(errorCode);
+
+ WKRetainPtr<WKURLRef> url = adoptWK(WKErrorCopyFailingURL(error));
+ if (url.get()) {
+ WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(url.get()));
+ stringBuilder.appendLiteral(", failing URL \"");
+ stringBuilder.append(toWTFString(urlString));
+ stringBuilder.append('"');
+ }
+
+ stringBuilder.append('>');
+}
+
+void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame);
+}
+
+void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame);
+}
+
+void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error);
+}
+
+void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame);
+}
+
+void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame);
+}
+
+void InjectedBundlePage::didFinishProgress(WKBundlePageRef, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishProgress();
+}
+
+void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishDocumentLoadForFrame(frame);
+}
+
+void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame, error);
+}
+
+void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame);
+}
+
+void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world);
+}
+
+void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCancelClientRedirectForFrame(frame);
+}
+
+void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willPerformClientRedirectForFrame(page, frame, url, delay, date);
+}
+
+void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didSameDocumentNavigationForFrame(frame, type);
+}
+
+void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didHandleOnloadEventsForFrame(frame);
+}
+
+void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDisplayInsecureContentForFrame(frame);
+}
+
+void InjectedBundlePage::didDetectXSSForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDetectXSSForFrame(frame);
+}
+
+void InjectedBundlePage::didRunInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didRunInsecureContentForFrame(frame);
+}
+
+void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, bool pageLoadIsProvisional, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didInitiateLoadForResource(page, frame, identifier, request, pageLoadIsProvisional);
+}
+
+WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSendRequestForFrame(page, frame, identifier, request, redirectResponse);
+}
+
+void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLResponseRef response, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveResponseForResource(page, frame, identifier, response);
+}
+
+void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, uint64_t length, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveContentLengthForResource(page, frame, identifier, length);
+}
+
+void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForResource(page, frame, identifier);
+}
+
+void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKErrorRef error, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadForResource(page, frame, identifier, error);
+}
+
+bool InjectedBundlePage::shouldCacheResponse(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldCacheResponse(page, frame, identifier);
+}
+
+void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (!injectedBundle.testRunner()->testURL()) {
+ WKRetainPtr<WKURLRef> testURL = adoptWK(WKBundleFrameCopyProvisionalURL(frame));
+ injectedBundle.testRunner()->setTestURL(testURL.get());
+ }
+
+ platformDidStartProvisionalLoadForFrame(frame);
+
+ if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ dumpLoadEvent(frame, "didStartProvisionalLoadForFrame");
+
+ if (!injectedBundle.topLoadingFrame())
+ injectedBundle.setTopLoadingFrame(frame);
+
+ if (injectedBundle.testRunner()->shouldStopProvisionalFrameLoads())
+ dumpLoadEvent(frame, "stopping load in didStartProvisionalLoadForFrame callback");
+}
+
+void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ return;
+
+ dumpLoadEvent(frame, "didReceiveServerRedirectForProvisionalLoadForFrame");
+}
+
+void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks()) {
+ dumpLoadEvent(frame, "didFailProvisionalLoadWithError");
+ if (WKErrorGetErrorCode(error) == kWKErrorCodeCannotShowURL)
+ dumpLoadEvent(frame, "(kWKErrorCodeCannotShowURL)");
+ }
+
+ frameDidChangeLocation(frame);
+}
+
+void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ return;
+
+ dumpLoadEvent(frame, "didCommitLoadForFrame");
+}
+
+void InjectedBundlePage::didFinishProgress()
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (!injectedBundle.testRunner()->shouldDumpProgressFinishedCallback())
+ return;
+
+ injectedBundle.outputText("postProgressFinishedNotification\n");
+}
+
+enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName };
+
+static void dumpFrameScrollPosition(WKBundleFrameRef frame, StringBuilder& stringBuilder, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName)
+{
+ double x = numericWindowPropertyValue(frame, "pageXOffset");
+ double y = numericWindowPropertyValue(frame, "pageYOffset");
+ if (fabs(x) <= 0.00000001 && fabs(y) <= 0.00000001)
+ return;
+
+ if (shouldIncludeFrameName) {
+ WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
+ stringBuilder.appendLiteral("frame '");
+ stringBuilder.append(toWTFString(name));
+ stringBuilder.appendLiteral("' ");
+ }
+ stringBuilder.appendLiteral("scrolled to ");
+ stringBuilder.append(WTF::String::number(x));
+ stringBuilder.append(',');
+ stringBuilder.append(WTF::String::number(y));
+ stringBuilder.append('\n');
+}
+
+static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame, StringBuilder& stringBuilder)
+{
+ WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
+ size_t size = WKArrayGetSize(childFrames.get());
+ for (size_t i = 0; i < size; ++i) {
+ WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
+ dumpFrameScrollPosition(subframe, stringBuilder, ShouldIncludeFrameName);
+ dumpDescendantFrameScrollPositions(subframe, stringBuilder);
+ }
+}
+
+void InjectedBundlePage::dumpAllFrameScrollPositions(StringBuilder& stringBuilder)
+{
+ WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
+ dumpFrameScrollPosition(frame, stringBuilder);
+ dumpDescendantFrameScrollPositions(frame, stringBuilder);
+}
+
+static JSRetainPtr<JSStringRef> toJS(const char* string)
+{
+ return JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString(string));
+}
+
+static bool hasDocumentElement(WKBundleFrameRef frame)
+{
+ JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
+ JSObjectRef globalObject = JSContextGetGlobalObject(context);
+
+ JSValueRef documentValue = JSObjectGetProperty(context, globalObject, toJS("document").get(), 0);
+ if (!documentValue)
+ return false;
+
+ ASSERT(JSValueIsObject(context, documentValue));
+ JSObjectRef document = JSValueToObject(context, documentValue, 0);
+
+ JSValueRef documentElementValue = JSObjectGetProperty(context, document, toJS("documentElement").get(), 0);
+ if (!documentElementValue)
+ return false;
+
+ return JSValueToBoolean(context, documentElementValue);
+}
+
+static void dumpFrameText(WKBundleFrameRef frame, StringBuilder& stringBuilder)
+{
+ // If the frame doesn't have a document element, its inner text will be an empty string, so
+ // we'll end up just appending a single newline below. But DumpRenderTree doesn't append
+ // anything in this case, so we shouldn't either.
+ if (!hasDocumentElement(frame))
+ return;
+
+ WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyInnerText(frame));
+ stringBuilder.append(toWTFString(text));
+ stringBuilder.append('\n');
+}
+
+static void dumpDescendantFramesText(WKBundleFrameRef frame, StringBuilder& stringBuilder)
+{
+ WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
+ size_t size = WKArrayGetSize(childFrames.get());
+ for (size_t i = 0; i < size; ++i) {
+ WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
+ WKRetainPtr<WKStringRef> subframeName(AdoptWK, WKBundleFrameCopyName(subframe));
+
+ // DumpRenderTree ignores empty frames, so do the same thing here.
+ if (!hasDocumentElement(subframe))
+ continue;
+
+ stringBuilder.appendLiteral("\n--------\nFrame: '");
+ stringBuilder.append(toWTFString(subframeName));
+ stringBuilder.appendLiteral("'\n--------\n");
+
+ dumpFrameText(subframe, stringBuilder);
+ dumpDescendantFramesText(subframe, stringBuilder);
+ }
+}
+
+void InjectedBundlePage::dumpAllFramesText(StringBuilder& stringBuilder)
+{
+ WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
+ dumpFrameText(frame, stringBuilder);
+ dumpDescendantFramesText(frame, stringBuilder);
+}
+
+
+void InjectedBundlePage::dumpDOMAsWebArchive(WKBundleFrameRef frame, StringBuilder& stringBuilder)
+{
+#if USE(CF)
+ WKRetainPtr<WKDataRef> wkData = adoptWK(WKBundleFrameCopyWebArchive(frame));
+ RetainPtr<CFDataRef> cfData = adoptCF(CFDataCreate(0, WKDataGetBytes(wkData.get()), WKDataGetSize(wkData.get())));
+ RetainPtr<CFStringRef> cfString = adoptCF(createXMLStringFromWebArchiveData(cfData.get()));
+ stringBuilder.append(cfString.get());
+#endif
+}
+
+void InjectedBundlePage::dump()
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ ASSERT(injectedBundle.isTestRunning());
+
+ // Force a paint before dumping. This matches DumpRenderTree on Windows. (DumpRenderTree on Mac
+ // does this at a slightly different time.) See <http://webkit.org/b/55469> for details.
+ WKBundlePageForceRepaint(m_page);
+
+ WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
+ WKRetainPtr<WKURLRef> urlRef = adoptWK(WKBundleFrameCopyURL(frame));
+ String url = toWTFString(adoptWK(WKURLCopyString(urlRef.get())));
+ WKRetainPtr<WKStringRef> mimeType = adoptWK(WKBundleFrameCopyMIMETypeForResourceWithURL(frame, urlRef.get()));
+ if (url.find("dumpAsText/") != notFound || WKStringIsEqualToUTF8CString(mimeType.get(), "text/plain"))
+ injectedBundle.testRunner()->dumpAsText(false);
+
+ StringBuilder stringBuilder;
+
+ switch (injectedBundle.testRunner()->whatToDump()) {
+ case TestRunner::RenderTree: {
+ if (injectedBundle.testRunner()->isPrinting())
+ stringBuilder.append(toWTFString(adoptWK(WKBundlePageCopyRenderTreeExternalRepresentationForPrinting(m_page)).get()));
+ else
+ stringBuilder.append(toWTFString(adoptWK(WKBundlePageCopyRenderTreeExternalRepresentation(m_page)).get()));
+ break;
+ }
+ case TestRunner::MainFrameText:
+ dumpFrameText(WKBundlePageGetMainFrame(m_page), stringBuilder);
+ break;
+ case TestRunner::AllFramesText:
+ dumpAllFramesText(stringBuilder);
+ break;
+ case TestRunner::Audio:
+ break;
+ case TestRunner::DOMAsWebArchive:
+ dumpDOMAsWebArchive(frame, stringBuilder);
+ break;
+ }
+
+ if (injectedBundle.testRunner()->shouldDumpAllFrameScrollPositions())
+ dumpAllFrameScrollPositions(stringBuilder);
+ else if (injectedBundle.testRunner()->shouldDumpMainFrameScrollPosition())
+ dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page), stringBuilder);
+
+ if (injectedBundle.testRunner()->shouldDumpBackForwardListsForAllWindows())
+ injectedBundle.dumpBackForwardListsForAllPages(stringBuilder);
+
+ if (injectedBundle.shouldDumpPixels() && injectedBundle.testRunner()->shouldDumpPixels()) {
+ WKSnapshotOptions options = kWKSnapshotOptionsShareable | kWKSnapshotOptionsInViewCoordinates;
+ if (injectedBundle.testRunner()->shouldDumpSelectionRect())
+ options |= kWKSnapshotOptionsPaintSelectionRectangle;
+
+ injectedBundle.setPixelResult(adoptWK(WKBundlePageCreateSnapshotWithOptions(m_page, WKBundleFrameGetVisibleContentBounds(WKBundlePageGetMainFrame(m_page)), options)).get());
+ if (WKBundlePageIsTrackingRepaints(m_page))
+ injectedBundle.setRepaintRects(adoptWK(WKBundlePageCopyTrackedRepaintRects(m_page)).get());
+ }
+
+ injectedBundle.outputText(stringBuilder.toString());
+ injectedBundle.done();
+}
+
+void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ dumpLoadEvent(frame, "didFinishLoadForFrame");
+
+ frameDidChangeLocation(frame, /*shouldDump*/ true);
+}
+
+void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ dumpLoadEvent(frame, "didFailLoadWithError");
+
+ frameDidChangeLocation(frame);
+}
+
+void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ StringBuilder stringBuilder;
+ if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks()) {
+ dumpFrameDescriptionSuitableForTestResult(frame, stringBuilder);
+ stringBuilder.appendLiteral(" - didReceiveTitle: ");
+ stringBuilder.append(toWTFString(title));
+ stringBuilder.append('\n');
+ }
+
+ if (injectedBundle.testRunner()->shouldDumpTitleChanges()) {
+ stringBuilder.appendLiteral("TITLE CHANGED: '");
+ stringBuilder.append(toWTFString(title));
+ stringBuilder.appendLiteral("'\n");
+ }
+
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
+ JSObjectRef window = JSContextGetGlobalObject(context);
+
+ if (WKBundleScriptWorldNormalWorld() != world) {
+ JSObjectSetProperty(context, window, toJS("__worldID").get(), JSValueMakeNumber(context, TestRunner::worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0);
+ return;
+ }
+
+ JSValueRef exception = nullptr;
+ injectedBundle.testRunner()->makeWindowObject(context, window, &exception);
+ injectedBundle.gcController()->makeWindowObject(context, window, &exception);
+ injectedBundle.eventSendingController()->makeWindowObject(context, window, &exception);
+ injectedBundle.textInputController()->makeWindowObject(context, window, &exception);
+ injectedBundle.accessibilityController()->makeWindowObject(context, window, &exception);
+
+ WebCoreTestSupport::injectInternalsObject(context);
+}
+
+void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ return;
+
+ dumpLoadEvent(frame, "didCancelClientRedirectForFrame");
+}
+
+void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKURLRef url, double delay, double date)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ return;
+
+ StringBuilder stringBuilder;
+ dumpFrameDescriptionSuitableForTestResult(frame, stringBuilder);
+ stringBuilder.appendLiteral(" - willPerformClientRedirectToURL: ");
+ stringBuilder.append(pathSuitableForTestResult(url));
+ stringBuilder.appendLiteral(" \n");
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ return;
+
+ if (type != kWKSameDocumentNavigationAnchorNavigation)
+ return;
+
+ dumpLoadEvent(frame, "didChangeLocationWithinPageForFrame");
+}
+
+void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ dumpLoadEvent(frame, "didFinishDocumentLoadForFrame");
+
+ unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame);
+ if (pendingFrameUnloadEvents) {
+ StringBuilder stringBuilder;
+ stringBuilder.append(frameToStr(frame));
+ stringBuilder.appendLiteral(" - has ");
+ stringBuilder.appendNumber(pendingFrameUnloadEvents);
+ stringBuilder.appendLiteral(" onunload handler(s)\n");
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+}
+
+void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ dumpLoadEvent(frame, "didHandleOnloadEventsForFrame");
+}
+
+void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundleFrameRef)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ injectedBundle.outputText("didDisplayInsecureContent\n");
+}
+
+void InjectedBundlePage::didRunInsecureContentForFrame(WKBundleFrameRef)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ injectedBundle.outputText("didRunInsecureContent\n");
+}
+
+void InjectedBundlePage::didDetectXSSForFrame(WKBundleFrameRef)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
+ injectedBundle.outputText("didDetectXSS\n");
+}
+
+void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef request, bool)
+{
+ if (!InjectedBundle::singleton().isTestRunning())
+ return;
+
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
+ assignedUrlsCache.add(identifier, pathSuitableForTestResult(url.get()));
+}
+
+// Resource Load Client Callbacks
+
+static inline bool isLocalHost(WKStringRef host)
+{
+ return WKStringIsEqualToUTF8CString(host, "127.0.0.1") || WKStringIsEqualToUTF8CString(host, "localhost");
+}
+
+static inline bool isHTTPOrHTTPSScheme(WKStringRef scheme)
+{
+ return WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "http") || WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "https");
+}
+
+static inline bool isAllowedHost(WKStringRef host)
+{
+ return InjectedBundle::singleton().isAllowedHost(host);
+}
+
+WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef response)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.isTestRunning()
+ && injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks()) {
+ StringBuilder stringBuilder;
+ dumpResourceURL(identifier, stringBuilder);
+ stringBuilder.appendLiteral(" - willSendRequest ");
+ dumpRequestDescriptionSuitableForTestResult(request, stringBuilder);
+ stringBuilder.appendLiteral(" redirectResponse ");
+ dumpResponseDescriptionSuitableForTestResult(response, stringBuilder);
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+
+ if (injectedBundle.isTestRunning() && injectedBundle.testRunner()->willSendRequestReturnsNull())
+ return nullptr;
+
+ WKRetainPtr<WKURLRef> redirectURL = adoptWK(WKURLResponseCopyURL(response));
+ if (injectedBundle.isTestRunning() && injectedBundle.testRunner()->willSendRequestReturnsNullOnRedirect() && redirectURL) {
+ injectedBundle.outputText("Returning null for this redirect\n");
+ return nullptr;
+ }
+
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
+ WKRetainPtr<WKStringRef> host = adoptWK(WKURLCopyHostName(url.get()));
+ WKRetainPtr<WKStringRef> scheme = adoptWK(WKURLCopyScheme(url.get()));
+ WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(url.get()));
+ if (host && !WKStringIsEmpty(host.get())
+ && isHTTPOrHTTPSScheme(scheme.get())
+ && !WKStringIsEqualToUTF8CString(host.get(), "255.255.255.255") // Used in some tests that expect to get back an error.
+ && !isLocalHost(host.get())) {
+ bool mainFrameIsExternal = false;
+ if (injectedBundle.isTestRunning()) {
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(m_page);
+ WKRetainPtr<WKURLRef> mainFrameURL = adoptWK(WKBundleFrameCopyURL(mainFrame));
+ if (!mainFrameURL || WKStringIsEqualToUTF8CString(adoptWK(WKURLCopyString(mainFrameURL.get())).get(), "about:blank"))
+ mainFrameURL = adoptWK(WKBundleFrameCopyProvisionalURL(mainFrame));
+
+ WKRetainPtr<WKStringRef> mainFrameHost = adoptWK(WKURLCopyHostName(mainFrameURL.get()));
+ WKRetainPtr<WKStringRef> mainFrameScheme = adoptWK(WKURLCopyScheme(mainFrameURL.get()));
+ mainFrameIsExternal = isHTTPOrHTTPSScheme(mainFrameScheme.get()) && !isLocalHost(mainFrameHost.get());
+ }
+ if (!mainFrameIsExternal && !isAllowedHost(host.get())) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("Blocked access to external URL ");
+ stringBuilder.append(toWTFString(urlString));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ return nullptr;
+ }
+ }
+
+ WKRetain(request);
+ return request;
+}
+
+void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLResponseRef response)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks()) {
+ StringBuilder stringBuilder;
+ dumpResourceURL(identifier, stringBuilder);
+ stringBuilder.appendLiteral(" - didReceiveResponse ");
+ dumpResponseDescriptionSuitableForTestResult(response, stringBuilder);
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+
+
+ if (!injectedBundle.testRunner()->shouldDumpResourceResponseMIMETypes())
+ return;
+
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLResponseCopyURL(response));
+ WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyLastPathComponent(url.get()));
+ WKRetainPtr<WKStringRef> mimeTypeString = adoptWK(WKURLResponseCopyMIMEType(response));
+
+ StringBuilder stringBuilder;
+ stringBuilder.append(toWTFString(urlString));
+ stringBuilder.appendLiteral(" has MIME type ");
+ stringBuilder.append(toWTFString(mimeTypeString));
+
+ String platformMimeType = platformResponseMimeType(response);
+ if (!platformMimeType.isEmpty() && platformMimeType != toWTFString(mimeTypeString)) {
+ stringBuilder.appendLiteral(" but platform response has ");
+ stringBuilder.append(platformMimeType);
+ }
+
+ stringBuilder.append('\n');
+
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, uint64_t)
+{
+}
+
+void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (!injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks())
+ return;
+
+ StringBuilder stringBuilder;
+ dumpResourceURL(identifier, stringBuilder);
+ stringBuilder.appendLiteral(" - didFinishLoading\n");
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKErrorRef error)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (!injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks())
+ return;
+
+ StringBuilder stringBuilder;
+ dumpResourceURL(identifier, stringBuilder);
+ stringBuilder.appendLiteral(" - didFailLoadingWithError: ");
+
+ dumpErrorDescriptionSuitableForTestResult(error, stringBuilder);
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+bool InjectedBundlePage::shouldCacheResponse(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return true;
+
+ if (!injectedBundle.testRunner()->shouldDumpWillCacheResponse())
+ return true;
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendNumber(identifier);
+ stringBuilder.appendLiteral(" - willCacheResponse: called\n");
+ injectedBundle.outputText(stringBuilder.toString());
+
+ // The default behavior is the cache the response.
+ return true;
+}
+
+
+// Policy Client Callbacks
+
+WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNavigationAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKTypeRef* userData, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(page, frame, navigationAction, request, userData);
+}
+
+WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKStringRef frameName, WKTypeRef* userData, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForNewWindowAction(page, frame, navigationAction, request, frameName, userData);
+}
+
+WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef page, WKBundleFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, WKTypeRef* userData, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForResponse(page, frame, response, request, userData);
+}
+
+void InjectedBundlePage::unableToImplementPolicy(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef* userData, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->unableToImplementPolicy(page, frame, error, userData);
+}
+
+WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNavigationAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKTypeRef* userData)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return WKBundlePagePolicyActionUse;
+
+ if (injectedBundle.testRunner()->shouldDumpPolicyCallbacks()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral(" - decidePolicyForNavigationAction \n");
+ dumpRequestDescriptionSuitableForTestResult(request, stringBuilder);
+ stringBuilder.appendLiteral(" is main frame - ");
+ stringBuilder.append(WKBundleFrameIsMainFrame(frame) ? "yes" : "no");
+ stringBuilder.appendLiteral(" should open URLs externally - ");
+ stringBuilder.append(WKBundleNavigationActionGetShouldOpenExternalURLs(navigationAction) ? "yes" : "no");
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+
+ if (!injectedBundle.testRunner()->isPolicyDelegateEnabled())
+ return WKBundlePagePolicyActionUse;
+
+ WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
+ WKRetainPtr<WKStringRef> urlScheme = adoptWK(WKURLCopyScheme(url.get()));
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("Policy delegate: attempt to load ");
+ if (isLocalFileScheme(urlScheme.get())) {
+ WKRetainPtr<WKStringRef> filename = adoptWK(WKURLCopyLastPathComponent(url.get()));
+ stringBuilder.append(toWTFString(filename));
+ } else {
+ WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(url.get()));
+ stringBuilder.append(toWTFString(urlString));
+ }
+ stringBuilder.appendLiteral(" with navigation type \'");
+ stringBuilder.append(toWTFString(NavigationTypeToString(WKBundleNavigationActionGetNavigationType(navigationAction))));
+ stringBuilder.appendLiteral("\'");
+ WKBundleHitTestResultRef hitTestResultRef = WKBundleNavigationActionCopyHitTestResult(navigationAction);
+ if (hitTestResultRef) {
+ stringBuilder.appendLiteral(" originating from ");
+ stringBuilder.append(dumpPath(m_page, m_world.get(), WKBundleHitTestResultCopyNodeHandle(hitTestResultRef)));
+ }
+
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ injectedBundle.testRunner()->notifyDone();
+
+ if (injectedBundle.testRunner()->isPolicyDelegatePermissive())
+ return WKBundlePagePolicyActionUse;
+ return WKBundlePagePolicyActionPassThrough;
+}
+
+WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef, WKStringRef, WKTypeRef*)
+{
+ return WKBundlePagePolicyActionUse;
+}
+
+WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef page, WKBundleFrameRef, WKURLResponseRef response, WKURLRequestRef, WKTypeRef*)
+{
+ if (InjectedBundle::singleton().testRunner()->isPolicyDelegateEnabled() && WKURLResponseIsAttachment(response)) {
+ StringBuilder stringBuilder;
+ WKRetainPtr<WKStringRef> filename = adoptWK(WKURLResponseCopySuggestedFilename(response));
+ stringBuilder.appendLiteral("Policy delegate: resource is an attachment, suggested file name \'");
+ stringBuilder.append(toWTFString(filename));
+ stringBuilder.appendLiteral("\'\n");
+ InjectedBundle::singleton().outputText(stringBuilder.toString());
+ }
+
+ WKRetainPtr<WKStringRef> mimeType = adoptWK(WKURLResponseCopyMIMEType(response));
+ return WKBundlePageCanShowMIMEType(page, mimeType.get()) ? WKBundlePagePolicyActionUse : WKBundlePagePolicyActionPassThrough;
+}
+
+void InjectedBundlePage::unableToImplementPolicy(WKBundlePageRef, WKBundleFrameRef, WKErrorRef, WKTypeRef*)
+{
+}
+
+// UI Client Callbacks
+
+void InjectedBundlePage::willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willAddMessageToConsole(message, lineNumber);
+}
+
+void InjectedBundlePage::willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSetStatusbarText(statusbarText);
+}
+
+void InjectedBundlePage::willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptAlert(message, frame);
+}
+
+void InjectedBundlePage::willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptConfirm(message, frame);
+}
+
+void InjectedBundlePage::willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame);
+}
+
+void InjectedBundlePage::didReachApplicationCacheOriginQuota(WKBundlePageRef page, WKSecurityOriginRef origin, int64_t totalBytesNeeded, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReachApplicationCacheOriginQuota(origin, totalBytesNeeded);
+}
+
+uint64_t InjectedBundlePage::didExceedDatabaseQuota(WKBundlePageRef page, WKSecurityOriginRef origin, WKStringRef databaseName, WKStringRef databaseDisplayName, uint64_t currentQuotaBytes, uint64_t currentOriginUsageBytes, uint64_t currentDatabaseUsageBytes, uint64_t expectedUsageBytes, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didExceedDatabaseQuota(origin, databaseName, databaseDisplayName, currentQuotaBytes, currentOriginUsageBytes, currentDatabaseUsageBytes, expectedUsageBytes);
+}
+
+static WTF::String lastFileURLPathComponent(const WTF::String& path)
+{
+ size_t pos = path.find("file://");
+ ASSERT(WTF::notFound != pos);
+
+ WTF::String tmpPath = path.substring(pos + 7);
+ if (tmpPath.length() < 2) // Keep the lone slash to avoid empty output.
+ return tmpPath;
+
+ // Remove the trailing delimiter
+ if (tmpPath[tmpPath.length() - 1] == '/')
+ tmpPath.remove(tmpPath.length() - 1);
+
+ pos = tmpPath.reverseFind('/');
+ if (WTF::notFound != pos)
+ return tmpPath.substring(pos + 1);
+
+ return tmpPath;
+}
+
+void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ WTF::String messageString = toWTFString(message);
+ size_t nullCharPos = messageString.find(UChar(0));
+ if (nullCharPos != WTF::notFound)
+ messageString.truncate(nullCharPos);
+
+ size_t fileProtocolStart = messageString.find("file://");
+ if (fileProtocolStart != WTF::notFound)
+ // FIXME: The code below does not handle additional text after url nor multiple urls. This matches DumpRenderTree implementation.
+ messageString = messageString.substring(0, fileProtocolStart) + lastFileURLPathComponent(messageString.substring(fileProtocolStart));
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("CONSOLE MESSAGE: ");
+ if (lineNumber) {
+ stringBuilder.appendLiteral("line ");
+ stringBuilder.appendNumber(lineNumber);
+ stringBuilder.appendLiteral(": ");
+ }
+ stringBuilder.append(messageString);
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (!injectedBundle.testRunner()->shouldDumpStatusCallbacks())
+ return;
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("UI DELEGATE STATUS CALLBACK: setStatusText:");
+ stringBuilder.append(toWTFString(statusbarText));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("ALERT: ");
+ stringBuilder.append(toWTFString(message));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("CONFIRM: ");
+ stringBuilder.append(toWTFString(message));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef)
+{
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("PROMPT: ");
+ stringBuilder.append(toWTFString(message));
+ stringBuilder.appendLiteral(", default text: ");
+ stringBuilder.append(toWTFString(defaultValue));
+ stringBuilder.append('\n');
+ InjectedBundle::singleton().outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::didReachApplicationCacheOriginQuota(WKSecurityOriginRef origin, int64_t totalBytesNeeded)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpApplicationCacheDelegateCallbacks()) {
+ // For example, numbers from 30000 - 39999 will output as 30000.
+ // Rounding up or down does not really matter for these tests. It's
+ // sufficient to just get a range of 10000 to determine if we were
+ // above or below a threshold.
+ int64_t truncatedSpaceNeeded = (totalBytesNeeded / 10000) * 10000;
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("UI DELEGATE APPLICATION CACHE CALLBACK: exceededApplicationCacheOriginQuotaForSecurityOrigin:");
+ stringBuilder.append(securityOriginToStr(origin));
+ stringBuilder.appendLiteral(" totalSpaceNeeded:~");
+ stringBuilder.appendNumber(truncatedSpaceNeeded);
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+
+ if (injectedBundle.testRunner()->shouldDisallowIncreaseForApplicationCacheQuota())
+ return;
+
+ // Reset default application cache quota.
+ WKBundleResetApplicationCacheOriginQuota(injectedBundle.bundle(), adoptWK(WKSecurityOriginCopyToString(origin)).get());
+}
+
+uint64_t InjectedBundlePage::didExceedDatabaseQuota(WKSecurityOriginRef origin, WKStringRef databaseName, WKStringRef databaseDisplayName, uint64_t currentQuotaBytes, uint64_t currentOriginUsageBytes, uint64_t currentDatabaseUsageBytes, uint64_t expectedUsageBytes)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpDatabaseCallbacks()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:");
+ stringBuilder.append(securityOriginToStr(origin));
+ stringBuilder.appendLiteral(" database:");
+ stringBuilder.append(toWTFString(databaseName));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+
+ uint64_t defaultQuota = 5 * 1024 * 1024;
+ double testDefaultQuota = injectedBundle.testRunner()->databaseDefaultQuota();
+ if (testDefaultQuota >= 0)
+ defaultQuota = testDefaultQuota;
+
+ unsigned long long newQuota = defaultQuota;
+
+ double maxQuota = injectedBundle.testRunner()->databaseMaxQuota();
+ if (maxQuota >= 0) {
+ if (defaultQuota < expectedUsageBytes && expectedUsageBytes <= maxQuota) {
+ newQuota = expectedUsageBytes;
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("UI DELEGATE DATABASE CALLBACK: increased quota to ");
+ stringBuilder.appendNumber(newQuota);
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+ }
+ return newQuota;
+}
+
+// Editor Client Callbacks
+
+bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldBeginEditing(range);
+}
+
+bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldEndEditing(range);
+}
+
+bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertNode(node, rangeToReplace, action);
+}
+
+bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertText(text, rangeToReplace, action);
+}
+
+bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldDeleteRange(range);
+}
+
+bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting);
+}
+
+bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo)
+{
+ return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldApplyStyle(style, range);
+}
+
+void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didBeginEditing(notificationName);
+}
+
+void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didEndEditing(notificationName);
+}
+
+void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChange(notificationName);
+}
+
+void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
+{
+ static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChangeSelection(notificationName);
+}
+
+bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return true;
+
+ if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: shouldBeginEditingInDOMRange:");
+ stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+ return injectedBundle.testRunner()->shouldAllowEditing();
+}
+
+bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return true;
+
+ if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: shouldEndEditingInDOMRange:");
+ stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+ return injectedBundle.testRunner()->shouldAllowEditing();
+}
+
+bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return true;
+
+ static const char* insertactionstring[] = {
+ "WebViewInsertActionTyped",
+ "WebViewInsertActionPasted",
+ "WebViewInsertActionDropped",
+ };
+
+ if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: shouldInsertNode:");
+ stringBuilder.append(dumpPath(m_page, m_world.get(), node));
+ stringBuilder.appendLiteral(" replacingDOMRange:");
+ stringBuilder.append(rangeToStr(m_page, m_world.get(), rangeToReplace));
+ stringBuilder.appendLiteral(" givenAction:");
+ stringBuilder.append(insertactionstring[action]);
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+ return injectedBundle.testRunner()->shouldAllowEditing();
+}
+
+bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return true;
+
+ static const char *insertactionstring[] = {
+ "WebViewInsertActionTyped",
+ "WebViewInsertActionPasted",
+ "WebViewInsertActionDropped",
+ };
+
+ if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: shouldInsertText:");
+ stringBuilder.append(toWTFString(text));
+ stringBuilder.appendLiteral(" replacingDOMRange:");
+ stringBuilder.append(rangeToStr(m_page, m_world.get(), rangeToReplace));
+ stringBuilder.appendLiteral(" givenAction:");
+ stringBuilder.append(insertactionstring[action]);
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+ return injectedBundle.testRunner()->shouldAllowEditing();
+}
+
+bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return true;
+
+ if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: shouldDeleteDOMRange:");
+ stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+ return injectedBundle.testRunner()->shouldAllowEditing();
+}
+
+bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return true;
+
+ static const char *affinitystring[] = {
+ "NSSelectionAffinityUpstream",
+ "NSSelectionAffinityDownstream"
+ };
+ static const char *boolstring[] = {
+ "FALSE",
+ "TRUE"
+ };
+
+ if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: shouldChangeSelectedDOMRange:");
+ stringBuilder.append(rangeToStr(m_page, m_world.get(), fromRange));
+ stringBuilder.appendLiteral(" toDOMRange:");
+ stringBuilder.append(rangeToStr(m_page, m_world.get(), toRange));
+ stringBuilder.appendLiteral(" affinity:");
+ stringBuilder.append(affinitystring[affinity]);
+ stringBuilder.appendLiteral(" stillSelecting:");
+ stringBuilder.append(boolstring[stillSelecting]);
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+ return injectedBundle.testRunner()->shouldAllowEditing();
+}
+
+bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return true;
+
+ if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: shouldApplyStyle:");
+ stringBuilder.append(styleDecToStr(style));
+ stringBuilder.appendLiteral(" toElementsInDOMRange:");
+ stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+ }
+ return injectedBundle.testRunner()->shouldAllowEditing();
+}
+
+void InjectedBundlePage::didBeginEditing(WKStringRef notificationName)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+ if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
+ return;
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidBeginEditing:");
+ stringBuilder.append(toWTFString(notificationName));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::didEndEditing(WKStringRef notificationName)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+ if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
+ return;
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidEndEditing:");
+ stringBuilder.append(toWTFString(notificationName));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::didChange(WKStringRef notificationName)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+ if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
+ return;
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidChange:");
+ stringBuilder.append(toWTFString(notificationName));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+void InjectedBundlePage::didChangeSelection(WKStringRef notificationName)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+ if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
+ return;
+
+ StringBuilder stringBuilder;
+ stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidChangeSelection:");
+ stringBuilder.append(toWTFString(notificationName));
+ stringBuilder.append('\n');
+ injectedBundle.outputText(stringBuilder.toString());
+}
+
+#if ENABLE(FULLSCREEN_API)
+bool InjectedBundlePage::supportsFullScreen(WKBundlePageRef pageRef, WKFullScreenKeyboardRequestType requestType)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
+ injectedBundle.outputText("supportsFullScreen() == true\n");
+ return true;
+}
+
+void InjectedBundlePage::enterFullScreenForElement(WKBundlePageRef pageRef, WKBundleNodeHandleRef elementRef)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
+ injectedBundle.outputText("enterFullScreenForElement()\n");
+
+ if (!injectedBundle.testRunner()->hasCustomFullScreenBehavior()) {
+ WKBundlePageWillEnterFullScreen(pageRef);
+ WKBundlePageDidEnterFullScreen(pageRef);
+ }
+}
+
+void InjectedBundlePage::exitFullScreenForElement(WKBundlePageRef pageRef, WKBundleNodeHandleRef elementRef)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
+ injectedBundle.outputText("exitFullScreenForElement()\n");
+
+ if (!injectedBundle.testRunner()->hasCustomFullScreenBehavior()) {
+ WKBundlePageWillExitFullScreen(pageRef);
+ WKBundlePageDidExitFullScreen(pageRef);
+ }
+}
+
+void InjectedBundlePage::beganEnterFullScreen(WKBundlePageRef, WKRect, WKRect)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
+ injectedBundle.outputText("beganEnterFullScreen()\n");
+}
+
+void InjectedBundlePage::beganExitFullScreen(WKBundlePageRef, WKRect, WKRect)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
+ injectedBundle.outputText("beganExitFullScreen()\n");
+}
+
+void InjectedBundlePage::closeFullScreen(WKBundlePageRef pageRef)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
+ injectedBundle.outputText("closeFullScreen()\n");
+
+ if (!injectedBundle.testRunner()->hasCustomFullScreenBehavior()) {
+ WKBundlePageWillExitFullScreen(pageRef);
+ WKBundlePageDidExitFullScreen(pageRef);
+ }
+}
+#endif
+
+static bool compareByTargetName(WKBundleBackForwardListItemRef item1, WKBundleBackForwardListItemRef item2)
+{
+ return toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item1))) < toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item2)));
+}
+
+static void dumpBackForwardListItem(WKBundleBackForwardListItemRef item, unsigned indent, bool isCurrentItem, StringBuilder& stringBuilder)
+{
+ unsigned column = 0;
+ if (isCurrentItem) {
+ stringBuilder.appendLiteral("curr->");
+ column = 6;
+ }
+ for (unsigned i = column; i < indent; i++)
+ stringBuilder.append(' ');
+
+ WTF::String url = toWTFString(adoptWK(WKURLCopyString(adoptWK(WKBundleBackForwardListItemCopyURL(item)).get())));
+ if (hasPrefix(url, "file:")) {
+ WTF::String directoryName = "/LayoutTests/";
+ size_t start = url.find(directoryName);
+ if (start == WTF::notFound)
+ start = 0;
+ else
+ start += directoryName.length();
+ stringBuilder.appendLiteral("(file test):");
+ stringBuilder.append(url.substring(start));
+ } else
+ stringBuilder.append(url);
+
+ WTF::String target = toWTFString(adoptWK(WKBundleBackForwardListItemCopyTarget(item)));
+ if (target.length()) {
+ stringBuilder.appendLiteral(" (in frame \"");
+ stringBuilder.append(target);
+ stringBuilder.appendLiteral("\")");
+ }
+
+ // FIXME: Need WKBackForwardListItemIsTargetItem.
+ if (WKBundleBackForwardListItemIsTargetItem(item))
+ stringBuilder.appendLiteral(" **nav target**");
+
+ stringBuilder.append('\n');
+
+ if (WKRetainPtr<WKArrayRef> kids = adoptWK(WKBundleBackForwardListItemCopyChildren(item))) {
+ // Sort to eliminate arbitrary result ordering which defeats reproducible testing.
+ size_t size = WKArrayGetSize(kids.get());
+ Vector<WKBundleBackForwardListItemRef> sortedKids(size);
+ for (size_t i = 0; i < size; ++i)
+ sortedKids[i] = static_cast<WKBundleBackForwardListItemRef>(WKArrayGetItemAtIndex(kids.get(), i));
+ stable_sort(sortedKids.begin(), sortedKids.end(), compareByTargetName);
+ for (size_t i = 0; i < size; ++i)
+ dumpBackForwardListItem(sortedKids[i], indent + 4, false, stringBuilder);
+ }
+}
+
+void InjectedBundlePage::dumpBackForwardList(StringBuilder& stringBuilder)
+{
+ stringBuilder.appendLiteral("\n============== Back Forward List ==============\n");
+
+ WKBundleBackForwardListRef list = WKBundlePageGetBackForwardList(m_page);
+
+ // Print out all items in the list after m_previousTestBackForwardListItem.
+ // Gather items from the end of the list, then print them out from oldest to newest.
+ Vector<WKRetainPtr<WKBundleBackForwardListItemRef> > itemsToPrint;
+ for (unsigned i = WKBundleBackForwardListGetForwardListCount(list); i; --i) {
+ WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
+ // Something is wrong if the item from the last test is in the forward part of the list.
+ ASSERT(!WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()));
+ itemsToPrint.append(item);
+ }
+
+ ASSERT(!WKBundleBackForwardListItemIsSame(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)).get(), m_previousTestBackForwardListItem.get()));
+
+ itemsToPrint.append(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)));
+
+ int currentItemIndex = itemsToPrint.size() - 1;
+
+ int backListCount = WKBundleBackForwardListGetBackListCount(list);
+ for (int i = -1; i >= -backListCount; --i) {
+ WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
+ if (WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()))
+ break;
+ itemsToPrint.append(item);
+ }
+
+ for (int i = itemsToPrint.size() - 1; i >= 0; i--)
+ dumpBackForwardListItem(itemsToPrint[i].get(), 8, i == currentItemIndex, stringBuilder);
+
+ stringBuilder.appendLiteral("===============================================\n");
+}
+
+#if !PLATFORM(COCOA)
+void InjectedBundlePage::platformDidStartProvisionalLoadForFrame(WKBundleFrameRef)
+{
+}
+
+String InjectedBundlePage::platformResponseMimeType(WKURLResponseRef)
+{
+ return String();
+}
+#endif
+
+void InjectedBundlePage::frameDidChangeLocation(WKBundleFrameRef frame, bool shouldDump)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (frame != injectedBundle.topLoadingFrame())
+ return;
+
+ injectedBundle.setTopLoadingFrame(nullptr);
+
+ if (injectedBundle.testRunner()->waitToDump())
+ return;
+
+ if (injectedBundle.shouldProcessWorkQueue()) {
+ injectedBundle.processWorkQueue();
+ return;
+ }
+
+ if (shouldDump)
+ injectedBundle.page()->dump();
+ else
+ injectedBundle.done();
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.h b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.h
new file mode 100644
index 000000000..746fc33c5
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef InjectedBundlePage_h
+#define InjectedBundlePage_h
+
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundleScriptWorld.h>
+#include <WebKit/WKRetainPtr.h>
+#include <wtf/text/WTFString.h>
+
+namespace WTR {
+
+class InjectedBundlePage {
+public:
+ InjectedBundlePage(WKBundlePageRef);
+ ~InjectedBundlePage();
+
+ WKBundlePageRef page() const { return m_page; }
+
+ void dump();
+
+ void stopLoading();
+
+ void prepare();
+ void resetAfterTest();
+
+ void dumpBackForwardList(WTF::StringBuilder&);
+
+private:
+ // Loader Client
+ static void didStartProvisionalLoadForFrame(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void*);
+ static void didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void*);
+ static void didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef, WKBundleFrameRef, WKErrorRef, WKTypeRef*, const void*);
+ static void didCommitLoadForFrame(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void*);
+ static void didFinishLoadForFrame(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void*);
+ static void didFinishProgress(WKBundlePageRef, const void*);
+ static void didFinishDocumentLoadForFrame(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void*);
+ static void didFailLoadWithErrorForFrame(WKBundlePageRef, WKBundleFrameRef, WKErrorRef, WKTypeRef*, const void*);
+ static void didReceiveTitleForFrame(WKBundlePageRef, WKStringRef title, WKBundleFrameRef, WKTypeRef*, const void*);
+ static void didClearWindowForFrame(WKBundlePageRef, WKBundleFrameRef, WKBundleScriptWorldRef, const void*);
+ static void didCancelClientRedirectForFrame(WKBundlePageRef, WKBundleFrameRef, const void*);
+ static void willPerformClientRedirectForFrame(WKBundlePageRef, WKBundleFrameRef, WKURLRef url, double delay, double date, const void*);
+ static void didSameDocumentNavigationForFrame(WKBundlePageRef, WKBundleFrameRef, WKSameDocumentNavigationType, WKTypeRef*, const void*);
+ static void didHandleOnloadEventsForFrame(WKBundlePageRef, WKBundleFrameRef, const void*);
+ static void didDisplayInsecureContentForFrame(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void*);
+ static void didRunInsecureContentForFrame(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void*);
+ static void didDetectXSSForFrame(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void*);
+ static void didInitiateLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef, bool pageLoadIsProvisional, const void*);
+ static WKURLRequestRef willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef, WKURLResponseRef, const void*);
+ static void didReceiveResponseForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKURLResponseRef, const void*);
+ static void didReceiveContentLengthForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, uint64_t length, const void*);
+ static void didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, const void*);
+ static void didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKErrorRef, const void*);
+ static bool shouldCacheResponse(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, const void*);
+
+ void didStartProvisionalLoadForFrame(WKBundleFrameRef);
+ void didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef);
+ void didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef, WKErrorRef);
+ void didCommitLoadForFrame(WKBundleFrameRef);
+ void didFinishLoadForFrame(WKBundleFrameRef);
+ void didFinishProgress();
+ void didFailLoadWithErrorForFrame(WKBundleFrameRef, WKErrorRef);
+ void didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef);
+ void didClearWindowForFrame(WKBundleFrameRef, WKBundleScriptWorldRef);
+ void didCancelClientRedirectForFrame(WKBundleFrameRef);
+ void willPerformClientRedirectForFrame(WKBundlePageRef, WKBundleFrameRef, WKURLRef, double delay, double date);
+ void didSameDocumentNavigationForFrame(WKBundleFrameRef, WKSameDocumentNavigationType);
+ void didFinishDocumentLoadForFrame(WKBundleFrameRef);
+ void didHandleOnloadEventsForFrame(WKBundleFrameRef);
+ void didDisplayInsecureContentForFrame(WKBundleFrameRef);
+ void didRunInsecureContentForFrame(WKBundleFrameRef);
+ void didDetectXSSForFrame(WKBundleFrameRef);
+
+ // Resource Load Client
+ void didInitiateLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef, bool pageLoadIsProvisional);
+ WKURLRequestRef willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef, WKURLResponseRef);
+ void didReceiveResponseForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKURLResponseRef);
+ void didReceiveContentLengthForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, uint64_t length);
+ void didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier);
+ void didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKErrorRef);
+ bool shouldCacheResponse(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier);
+
+ // WKBundlePagePolicyClient
+ static WKBundlePagePolicyAction decidePolicyForNavigationAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef, WKTypeRef*, const void*);
+ static WKBundlePagePolicyAction decidePolicyForNewWindowAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef, WKStringRef frameName, WKTypeRef*, const void*);
+ static WKBundlePagePolicyAction decidePolicyForResponse(WKBundlePageRef, WKBundleFrameRef, WKURLResponseRef, WKURLRequestRef, WKTypeRef*, const void*);
+ static void unableToImplementPolicy(WKBundlePageRef, WKBundleFrameRef, WKErrorRef, WKTypeRef*, const void*);
+ WKBundlePagePolicyAction decidePolicyForNavigationAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef, WKTypeRef*);
+ WKBundlePagePolicyAction decidePolicyForNewWindowAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef, WKStringRef frameName, WKTypeRef*);
+ WKBundlePagePolicyAction decidePolicyForResponse(WKBundlePageRef, WKBundleFrameRef, WKURLResponseRef, WKURLRequestRef, WKTypeRef*);
+ void unableToImplementPolicy(WKBundlePageRef, WKBundleFrameRef, WKErrorRef, WKTypeRef*);
+
+ // UI Client
+ static void willAddMessageToConsole(WKBundlePageRef, WKStringRef message, uint32_t lineNumber, const void* clientInfo);
+ static void willSetStatusbarText(WKBundlePageRef, WKStringRef statusbarText, const void* clientInfo);
+ static void willRunJavaScriptAlert(WKBundlePageRef, WKStringRef message, WKBundleFrameRef frame, const void* clientInfo);
+ static void willRunJavaScriptConfirm(WKBundlePageRef, WKStringRef message, WKBundleFrameRef frame, const void* clientInfo);
+ static void willRunJavaScriptPrompt(WKBundlePageRef, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void* clientInfo);
+ static void didReachApplicationCacheOriginQuota(WKBundlePageRef, WKSecurityOriginRef, int64_t totalBytesNeeded, const void* clientInfo);
+ static uint64_t didExceedDatabaseQuota(WKBundlePageRef, WKSecurityOriginRef, WKStringRef databaseName, WKStringRef databaseDisplayName, uint64_t currentQuotaBytes, uint64_t currentOriginUsageBytes, uint64_t currentDatabaseUsageBytes, uint64_t expectedUsageBytes, const void* clientInfo);
+ void willAddMessageToConsole(WKStringRef message, uint32_t lineNumber);
+ void willSetStatusbarText(WKStringRef statusbarText);
+ void willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef);
+ void willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef);
+ void willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef);
+ void didReachApplicationCacheOriginQuota(WKSecurityOriginRef, int64_t totalBytesNeeded);
+ uint64_t didExceedDatabaseQuota(WKSecurityOriginRef, WKStringRef databaseName, WKStringRef databaseDisplayName, uint64_t currentQuotaBytes, uint64_t currentOriginUsageBytes, uint64_t currentDatabaseUsageBytes, uint64_t expectedUsageBytes);
+
+#if ENABLE(FULLSCREEN_API)
+ // Full Screen client
+ static bool supportsFullScreen(WKBundlePageRef, WKFullScreenKeyboardRequestType);
+ static void setHasCustomFullScreenBehavior(WKBundlePageRef, bool value);
+ static void enterFullScreenForElement(WKBundlePageRef, WKBundleNodeHandleRef element);
+ static void exitFullScreenForElement(WKBundlePageRef, WKBundleNodeHandleRef element);
+ static void beganEnterFullScreen(WKBundlePageRef, WKRect initialFrame, WKRect finalFrame);
+ static void beganExitFullScreen(WKBundlePageRef, WKRect initialFrame, WKRect finalFrame);
+ static void closeFullScreen(WKBundlePageRef);
+#endif
+
+ // Editor client
+ static bool shouldBeginEditing(WKBundlePageRef, WKBundleRangeHandleRef, const void* clientInfo);
+ static bool shouldEndEditing(WKBundlePageRef, WKBundleRangeHandleRef, const void* clientInfo);
+ static bool shouldInsertNode(WKBundlePageRef, WKBundleNodeHandleRef, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType, const void* clientInfo);
+ static bool shouldInsertText(WKBundlePageRef, WKStringRef, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType, const void* clientInfo);
+ static bool shouldDeleteRange(WKBundlePageRef, WKBundleRangeHandleRef, const void* clientInfo);
+ static bool shouldChangeSelectedRange(WKBundlePageRef, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType, bool stillSelecting, const void* clientInfo);
+ static bool shouldApplyStyle(WKBundlePageRef, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo);
+ static void didBeginEditing(WKBundlePageRef, WKStringRef notificationName, const void* clientInfo);
+ static void didEndEditing(WKBundlePageRef, WKStringRef notificationName, const void* clientInfo);
+ static void didChange(WKBundlePageRef, WKStringRef notificationName, const void* clientInfo);
+ static void didChangeSelection(WKBundlePageRef, WKStringRef notificationName, const void* clientInfo);
+ bool shouldBeginEditing(WKBundleRangeHandleRef);
+ bool shouldEndEditing(WKBundleRangeHandleRef);
+ bool shouldInsertNode(WKBundleNodeHandleRef, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType);
+ bool shouldInsertText(WKStringRef, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType);
+ bool shouldDeleteRange(WKBundleRangeHandleRef);
+ bool shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType, bool stillSelecting);
+ bool shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range);
+ void didBeginEditing(WKStringRef notificationName);
+ void didEndEditing(WKStringRef notificationName);
+ void didChange(WKStringRef notificationName);
+ void didChangeSelection(WKStringRef notificationName);
+
+ void dumpAllFramesText(WTF::StringBuilder&);
+ void dumpAllFrameScrollPositions(WTF::StringBuilder&);
+ void dumpDOMAsWebArchive(WKBundleFrameRef, WTF::StringBuilder&);
+
+ void platformDidStartProvisionalLoadForFrame(WKBundleFrameRef);
+ String platformResponseMimeType(WKURLResponseRef);
+
+ void frameDidChangeLocation(WKBundleFrameRef, bool shouldDump = false);
+
+ WKBundlePageRef m_page;
+ WKRetainPtr<WKBundleScriptWorldRef> m_world;
+ WKRetainPtr<WKBundleBackForwardListItemRef> m_previousTestBackForwardListItem;
+};
+
+} // namespace WTR
+
+#endif // InjectedBundlePage_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
new file mode 100644
index 000000000..d9c32ef39
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
@@ -0,0 +1,859 @@
+/*
+ * Copyright (C) 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestRunner.h"
+
+#include "InjectedBundle.h"
+#include "InjectedBundlePage.h"
+#include "JSTestRunner.h"
+#include "PlatformWebView.h"
+#include "StringFunctions.h"
+#include "TestController.h"
+#include <JavaScriptCore/JSCTestRunnerUtils.h>
+#include <WebKit/WKBundle.h>
+#include <WebKit/WKBundleBackForwardList.h>
+#include <WebKit/WKBundleFrame.h>
+#include <WebKit/WKBundleFramePrivate.h>
+#include <WebKit/WKBundleInspector.h>
+#include <WebKit/WKBundleNodeHandlePrivate.h>
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundlePagePrivate.h>
+#include <WebKit/WKBundlePrivate.h>
+#include <WebKit/WKBundleScriptWorld.h>
+#include <WebKit/WKData.h>
+#include <WebKit/WKRetainPtr.h>
+#include <WebKit/WKSerializedScriptValue.h>
+#include <WebKit/WebKit2_C.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/HashMap.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WTR {
+
+PassRefPtr<TestRunner> TestRunner::create()
+{
+ return adoptRef(new TestRunner);
+}
+
+TestRunner::TestRunner()
+ : m_whatToDump(RenderTree)
+ , m_shouldDumpAllFrameScrollPositions(false)
+ , m_shouldDumpBackForwardListsForAllWindows(false)
+ , m_shouldAllowEditing(true)
+ , m_shouldCloseExtraWindows(false)
+ , m_dumpEditingCallbacks(false)
+ , m_dumpStatusCallbacks(false)
+ , m_dumpTitleChanges(false)
+ , m_dumpPixels(true)
+ , m_dumpSelectionRect(false)
+ , m_dumpFullScreenCallbacks(false)
+ , m_dumpFrameLoadCallbacks(false)
+ , m_dumpProgressFinishedCallback(false)
+ , m_dumpResourceLoadCallbacks(false)
+ , m_dumpResourceResponseMIMETypes(false)
+ , m_dumpWillCacheResponse(false)
+ , m_dumpApplicationCacheDelegateCallbacks(false)
+ , m_dumpDatabaseCallbacks(false)
+ , m_disallowIncreaseForApplicationCacheQuota(false)
+ , m_waitToDump(false)
+ , m_testRepaint(false)
+ , m_testRepaintSweepHorizontally(false)
+ , m_isPrinting(false)
+ , m_willSendRequestReturnsNull(false)
+ , m_willSendRequestReturnsNullOnRedirect(false)
+ , m_shouldStopProvisionalFrameLoads(false)
+ , m_policyDelegateEnabled(false)
+ , m_policyDelegatePermissive(false)
+ , m_globalFlag(false)
+ , m_customFullScreenBehavior(false)
+ , m_timeout(30000)
+ , m_databaseDefaultQuota(-1)
+ , m_databaseMaxQuota(-1)
+ , m_userStyleSheetEnabled(false)
+ , m_userStyleSheetLocation(adoptWK(WKStringCreateWithUTF8CString("")))
+{
+ platformInitialize();
+}
+
+TestRunner::~TestRunner()
+{
+}
+
+JSClassRef TestRunner::wrapperClass()
+{
+ return JSTestRunner::testRunnerClass();
+}
+
+void TestRunner::display()
+{
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ WKBundlePageForceRepaint(page);
+ WKBundlePageSetTracksRepaints(page, true);
+ WKBundlePageResetTrackedRepaints(page);
+}
+
+void TestRunner::dumpAsText(bool dumpPixels)
+{
+ if (m_whatToDump < MainFrameText)
+ m_whatToDump = MainFrameText;
+ m_dumpPixels = dumpPixels;
+}
+
+void TestRunner::setCustomPolicyDelegate(bool enabled, bool permissive)
+{
+ m_policyDelegateEnabled = enabled;
+ m_policyDelegatePermissive = permissive;
+
+ InjectedBundle::singleton().setCustomPolicyDelegate(enabled, permissive);
+}
+
+void TestRunner::waitForPolicyDelegate()
+{
+ setCustomPolicyDelegate(true);
+ waitUntilDone();
+}
+
+void TestRunner::waitUntilDone()
+{
+ m_waitToDump = true;
+ if (InjectedBundle::singleton().useWaitToDumpWatchdogTimer())
+ initializeWaitToDumpWatchdogTimerIfNeeded();
+}
+
+void TestRunner::waitToDumpWatchdogTimerFired()
+{
+ invalidateWaitToDumpWatchdogTimer();
+ auto& injectedBundle = InjectedBundle::singleton();
+ injectedBundle.outputText("FAIL: Timed out waiting for notifyDone to be called\n\n");
+ injectedBundle.done();
+}
+
+void TestRunner::notifyDone()
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ if (!injectedBundle.isTestRunning())
+ return;
+
+ if (m_waitToDump && !injectedBundle.topLoadingFrame())
+ injectedBundle.page()->dump();
+
+ // We don't call invalidateWaitToDumpWatchdogTimer() here, even if we continue to wait for a load to finish.
+ // The test is still subject to timeout checking - it is better to detect an async timeout inside WebKitTestRunner
+ // than to let webkitpy do that, because WebKitTestRunner will dump partial results.
+
+ m_waitToDump = false;
+}
+
+void TestRunner::addUserScript(JSStringRef source, bool runAtStart, bool allFrames)
+{
+ WKRetainPtr<WKStringRef> sourceWK = toWK(source);
+ WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
+
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleAddUserScript(injectedBundle.bundle(), injectedBundle.pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
+ (runAtStart ? kWKInjectAtDocumentStart : kWKInjectAtDocumentEnd),
+ (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
+}
+
+void TestRunner::addUserStyleSheet(JSStringRef source, bool allFrames)
+{
+ WKRetainPtr<WKStringRef> sourceWK = toWK(source);
+ WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
+
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleAddUserStyleSheet(injectedBundle.bundle(), injectedBundle.pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
+ (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
+}
+
+void TestRunner::keepWebHistory()
+{
+ InjectedBundle::singleton().postSetAddsVisitedLinks(true);
+}
+
+void TestRunner::execCommand(JSStringRef name, JSStringRef argument)
+{
+ WKBundlePageExecuteEditingCommand(InjectedBundle::singleton().page()->page(), toWK(name).get(), toWK(argument).get());
+}
+
+bool TestRunner::findString(JSStringRef target, JSValueRef optionsArrayAsValue)
+{
+ WKFindOptions options = 0;
+
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+ JSRetainPtr<JSStringRef> lengthPropertyName(Adopt, JSStringCreateWithUTF8CString("length"));
+ JSObjectRef optionsArray = JSValueToObject(context, optionsArrayAsValue, 0);
+ JSValueRef lengthValue = JSObjectGetProperty(context, optionsArray, lengthPropertyName.get(), 0);
+ if (!JSValueIsNumber(context, lengthValue))
+ return false;
+
+ size_t length = static_cast<size_t>(JSValueToNumber(context, lengthValue, 0));
+ for (size_t i = 0; i < length; ++i) {
+ JSValueRef value = JSObjectGetPropertyAtIndex(context, optionsArray, i, 0);
+ if (!JSValueIsString(context, value))
+ continue;
+
+ JSRetainPtr<JSStringRef> optionName(Adopt, JSValueToStringCopy(context, value, 0));
+
+ if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive"))
+ options |= kWKFindOptionsCaseInsensitive;
+ else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts"))
+ options |= kWKFindOptionsAtWordStarts;
+ else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart"))
+ options |= kWKFindOptionsTreatMedialCapitalAsWordStart;
+ else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards"))
+ options |= kWKFindOptionsBackwards;
+ else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround"))
+ options |= kWKFindOptionsWrapAround;
+ else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection")) {
+ // FIXME: No kWKFindOptionsStartInSelection.
+ }
+ }
+
+ return WKBundlePageFindString(injectedBundle.page()->page(), toWK(target).get(), options);
+}
+
+void TestRunner::clearAllDatabases()
+{
+ WKBundleClearAllDatabases(InjectedBundle::singleton().bundle());
+}
+
+void TestRunner::setDatabaseQuota(uint64_t quota)
+{
+ return WKBundleSetDatabaseQuota(InjectedBundle::singleton().bundle(), quota);
+}
+
+void TestRunner::clearAllApplicationCaches()
+{
+ WKBundleClearApplicationCache(InjectedBundle::singleton().bundle());
+}
+
+void TestRunner::clearApplicationCacheForOrigin(JSStringRef origin)
+{
+ WKBundleClearApplicationCacheForOrigin(InjectedBundle::singleton().bundle(), toWK(origin).get());
+}
+
+void TestRunner::setAppCacheMaximumSize(uint64_t size)
+{
+ WKBundleSetAppCacheMaximumSize(InjectedBundle::singleton().bundle(), size);
+}
+
+long long TestRunner::applicationCacheDiskUsageForOrigin(JSStringRef origin)
+{
+ return WKBundleGetAppCacheUsageForOrigin(InjectedBundle::singleton().bundle(), toWK(origin).get());
+}
+
+void TestRunner::disallowIncreaseForApplicationCacheQuota()
+{
+ m_disallowIncreaseForApplicationCacheQuota = true;
+}
+
+static inline JSValueRef stringArrayToJS(JSContextRef context, WKArrayRef strings)
+{
+ const size_t count = WKArrayGetSize(strings);
+
+ JSValueRef arrayResult = JSObjectMakeArray(context, 0, 0, 0);
+ JSObjectRef arrayObj = JSValueToObject(context, arrayResult, 0);
+ for (size_t i = 0; i < count; ++i) {
+ WKStringRef stringRef = static_cast<WKStringRef>(WKArrayGetItemAtIndex(strings, i));
+ JSRetainPtr<JSStringRef> stringJS = toJS(stringRef);
+ JSObjectSetPropertyAtIndex(context, arrayObj, i, JSValueMakeString(context, stringJS.get()), 0);
+ }
+
+ return arrayResult;
+}
+
+JSValueRef TestRunner::originsWithApplicationCache()
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKRetainPtr<WKArrayRef> origins(AdoptWK, WKBundleCopyOriginsWithApplicationCache(injectedBundle.bundle()));
+
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+
+ return stringArrayToJS(context, origins.get());
+}
+
+bool TestRunner::isCommandEnabled(JSStringRef name)
+{
+ return WKBundlePageIsEditingCommandEnabled(InjectedBundle::singleton().page()->page(), toWK(name).get());
+}
+
+void TestRunner::setCanOpenWindows(bool)
+{
+ // It's not clear if or why any tests require opening windows be forbidden.
+ // For now, just ignore this setting, and if we find later it's needed we can add it.
+}
+
+void TestRunner::setXSSAuditorEnabled(bool enabled)
+{
+ WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitXSSAuditorEnabled"));
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), key.get(), enabled);
+}
+
+void TestRunner::setAllowUniversalAccessFromFileURLs(bool enabled)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetAllowUniversalAccessFromFileURLs(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
+}
+
+void TestRunner::setAllowFileAccessFromFileURLs(bool enabled)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetAllowFileAccessFromFileURLs(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
+}
+
+void TestRunner::setPluginsEnabled(bool enabled)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetPluginsEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
+}
+
+void TestRunner::setJavaScriptCanAccessClipboard(bool enabled)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetJavaScriptCanAccessClipboard(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
+}
+
+void TestRunner::setPrivateBrowsingEnabled(bool enabled)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetPrivateBrowsingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
+}
+
+void TestRunner::setPopupBlockingEnabled(bool enabled)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetPopupBlockingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
+}
+
+void TestRunner::setAuthorAndUserStylesEnabled(bool enabled)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetAuthorAndUserStylesEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
+}
+
+void TestRunner::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
+{
+ WKBundleAddOriginAccessWhitelistEntry(InjectedBundle::singleton().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
+}
+
+void TestRunner::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
+{
+ WKBundleRemoveOriginAccessWhitelistEntry(InjectedBundle::singleton().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
+}
+
+bool TestRunner::isPageBoxVisible(int pageIndex)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
+ return WKBundleIsPageBoxVisible(injectedBundle.bundle(), mainFrame, pageIndex);
+}
+
+void TestRunner::setValueForUser(JSContextRef context, JSValueRef element, JSStringRef value)
+{
+ if (!element || !JSValueIsObject(context, element))
+ return;
+
+ WKRetainPtr<WKBundleNodeHandleRef> nodeHandle(AdoptWK, WKBundleNodeHandleCreate(context, const_cast<JSObjectRef>(element)));
+ WKBundleNodeHandleSetHTMLInputElementValueForUser(nodeHandle.get(), toWK(value).get());
+}
+
+void TestRunner::setAudioResult(JSContextRef context, JSValueRef data)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ // FIXME (123058): Use a JSC API to get buffer contents once such is exposed.
+ WKRetainPtr<WKDataRef> audioData(AdoptWK, WKBundleCreateWKDataFromUInt8Array(injectedBundle.bundle(), context, data));
+ injectedBundle.setAudioResult(audioData.get());
+ m_whatToDump = Audio;
+ m_dumpPixels = false;
+}
+
+unsigned TestRunner::windowCount()
+{
+ return InjectedBundle::singleton().pageCount();
+}
+
+void TestRunner::clearBackForwardList()
+{
+ WKBundleBackForwardListClear(WKBundlePageGetBackForwardList(InjectedBundle::singleton().page()->page()));
+}
+
+// Object Creation
+
+void TestRunner::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
+{
+ setProperty(context, windowObject, "testRunner", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
+}
+
+void TestRunner::showWebInspector()
+{
+ WKBundleInspectorShow(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()));
+}
+
+void TestRunner::closeWebInspector()
+{
+ WKBundleInspectorClose(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()));
+}
+
+void TestRunner::evaluateInWebInspector(JSStringRef script)
+{
+ WKRetainPtr<WKStringRef> scriptWK = toWK(script);
+ WKBundleInspectorEvaluateScriptForTest(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()), scriptWK.get());
+}
+
+typedef WTF::HashMap<unsigned, WKRetainPtr<WKBundleScriptWorldRef> > WorldMap;
+static WorldMap& worldMap()
+{
+ static WorldMap& map = *new WorldMap;
+ return map;
+}
+
+unsigned TestRunner::worldIDForWorld(WKBundleScriptWorldRef world)
+{
+ WorldMap::const_iterator end = worldMap().end();
+ for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) {
+ if (it->value == world)
+ return it->key;
+ }
+
+ return 0;
+}
+
+void TestRunner::evaluateScriptInIsolatedWorld(JSContextRef context, unsigned worldID, JSStringRef script)
+{
+ // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world
+ // that is created once and cached forever.
+ WKRetainPtr<WKBundleScriptWorldRef> world;
+ if (!worldID)
+ world.adopt(WKBundleScriptWorldCreateWorld());
+ else {
+ WKRetainPtr<WKBundleScriptWorldRef>& worldSlot = worldMap().add(worldID, nullptr).iterator->value;
+ if (!worldSlot)
+ worldSlot.adopt(WKBundleScriptWorldCreateWorld());
+ world = worldSlot;
+ }
+
+ WKBundleFrameRef frame = WKBundleFrameForJavaScriptContext(context);
+ if (!frame)
+ frame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
+
+ JSGlobalContextRef jsContext = WKBundleFrameGetJavaScriptContextForWorld(frame, world.get());
+ JSEvaluateScript(jsContext, script, 0, 0, 0, 0);
+}
+
+void TestRunner::setPOSIXLocale(JSStringRef locale)
+{
+ char localeBuf[32];
+ JSStringGetUTF8CString(locale, localeBuf, sizeof(localeBuf));
+ setlocale(LC_ALL, localeBuf);
+}
+
+void TestRunner::setTextDirection(JSStringRef direction)
+{
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
+ return WKBundleFrameSetTextDirection(mainFrame, toWK(direction).get());
+}
+
+void TestRunner::setShouldStayOnPageAfterHandlingBeforeUnload(bool shouldStayOnPage)
+{
+ InjectedBundle::singleton().postNewBeforeUnloadReturnValue(!shouldStayOnPage);
+}
+
+void TestRunner::setDefersLoading(bool shouldDeferLoading)
+{
+ WKBundlePageSetDefersLoading(InjectedBundle::singleton().page()->page(), shouldDeferLoading);
+}
+
+void TestRunner::setPageVisibility(JSStringRef state)
+{
+ InjectedBundle::singleton().setHidden(JSStringIsEqualToUTF8CString(state, "hidden") || JSStringIsEqualToUTF8CString(state, "prerender"));
+}
+
+void TestRunner::resetPageVisibility()
+{
+ InjectedBundle::singleton().setHidden(false);
+}
+
+typedef WTF::HashMap<unsigned, JSValueRef> CallbackMap;
+static CallbackMap& callbackMap()
+{
+ static CallbackMap& map = *new CallbackMap;
+ return map;
+}
+
+enum {
+ AddChromeInputFieldCallbackID = 1,
+ RemoveChromeInputFieldCallbackID,
+ FocusWebViewCallbackID,
+ SetBackingScaleFactorCallbackID
+};
+
+static void cacheTestRunnerCallback(unsigned index, JSValueRef callback)
+{
+ if (!callback)
+ return;
+
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+ JSValueProtect(context, callback);
+ callbackMap().add(index, callback);
+}
+
+static void callTestRunnerCallback(unsigned index)
+{
+ if (!callbackMap().contains(index))
+ return;
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+ JSObjectRef callback = JSValueToObject(context, callbackMap().take(index), 0);
+ JSObjectCallAsFunction(context, callback, JSContextGetGlobalObject(context), 0, 0, 0);
+ JSValueUnprotect(context, callback);
+}
+
+void TestRunner::addChromeInputField(JSValueRef callback)
+{
+ cacheTestRunnerCallback(AddChromeInputFieldCallbackID, callback);
+ InjectedBundle::singleton().postAddChromeInputField();
+}
+
+void TestRunner::removeChromeInputField(JSValueRef callback)
+{
+ cacheTestRunnerCallback(RemoveChromeInputFieldCallbackID, callback);
+ InjectedBundle::singleton().postRemoveChromeInputField();
+}
+
+void TestRunner::focusWebView(JSValueRef callback)
+{
+ cacheTestRunnerCallback(FocusWebViewCallbackID, callback);
+ InjectedBundle::singleton().postFocusWebView();
+}
+
+void TestRunner::setBackingScaleFactor(double backingScaleFactor, JSValueRef callback)
+{
+ cacheTestRunnerCallback(SetBackingScaleFactorCallbackID, callback);
+ InjectedBundle::singleton().postSetBackingScaleFactor(backingScaleFactor);
+}
+
+void TestRunner::setWindowIsKey(bool isKey)
+{
+ InjectedBundle::singleton().postSetWindowIsKey(isKey);
+}
+
+void TestRunner::callAddChromeInputFieldCallback()
+{
+ callTestRunnerCallback(AddChromeInputFieldCallbackID);
+}
+
+void TestRunner::callRemoveChromeInputFieldCallback()
+{
+ callTestRunnerCallback(RemoveChromeInputFieldCallbackID);
+}
+
+void TestRunner::callFocusWebViewCallback()
+{
+ callTestRunnerCallback(FocusWebViewCallbackID);
+}
+
+void TestRunner::callSetBackingScaleFactorCallback()
+{
+ callTestRunnerCallback(SetBackingScaleFactorCallbackID);
+}
+
+static inline bool toBool(JSStringRef value)
+{
+ return JSStringIsEqualToUTF8CString(value, "true") || JSStringIsEqualToUTF8CString(value, "1");
+}
+
+void TestRunner::overridePreference(JSStringRef preference, JSStringRef value)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ // FIXME: handle non-boolean preferences.
+ WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), toWK(preference).get(), toBool(value));
+}
+
+void TestRunner::setAlwaysAcceptCookies(bool accept)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAlwaysAcceptCookies"));
+
+ WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(accept));
+
+ WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), 0);
+}
+
+double TestRunner::preciseTime()
+{
+ return currentTime();
+}
+
+void TestRunner::setUserStyleSheetEnabled(bool enabled)
+{
+ m_userStyleSheetEnabled = enabled;
+
+ WKRetainPtr<WKStringRef> emptyUrl = adoptWK(WKStringCreateWithUTF8CString(""));
+ WKStringRef location = enabled ? m_userStyleSheetLocation.get() : emptyUrl.get();
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetUserStyleSheetLocation(injectedBundle.bundle(), injectedBundle.pageGroup(), location);
+}
+
+void TestRunner::setUserStyleSheetLocation(JSStringRef location)
+{
+ m_userStyleSheetLocation = adoptWK(WKStringCreateWithJSString(location));
+
+ if (m_userStyleSheetEnabled)
+ setUserStyleSheetEnabled(true);
+}
+
+void TestRunner::setSpatialNavigationEnabled(bool enabled)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetSpatialNavigationEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
+}
+
+void TestRunner::setTabKeyCyclesThroughElements(bool enabled)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetTabKeyCyclesThroughElements(injectedBundle.bundle(), injectedBundle.page()->page(), enabled);
+}
+
+void TestRunner::setSerializeHTTPLoads()
+{
+ // WK2 doesn't reorder loads.
+}
+
+void TestRunner::dispatchPendingLoadRequests()
+{
+ // WK2 doesn't keep pending requests.
+}
+
+void TestRunner::setCacheModel(int model)
+{
+ InjectedBundle::singleton().setCacheModel(model);
+}
+
+void TestRunner::setAsynchronousSpellCheckingEnabled(bool enabled)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetAsynchronousSpellCheckingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
+}
+
+void TestRunner::grantWebNotificationPermission(JSStringRef origin)
+{
+ WKRetainPtr<WKStringRef> originWK = toWK(origin);
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetWebNotificationPermission(injectedBundle.bundle(), injectedBundle.page()->page(), originWK.get(), true);
+}
+
+void TestRunner::denyWebNotificationPermission(JSStringRef origin)
+{
+ WKRetainPtr<WKStringRef> originWK = toWK(origin);
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleSetWebNotificationPermission(injectedBundle.bundle(), injectedBundle.page()->page(), originWK.get(), false);
+}
+
+void TestRunner::removeAllWebNotificationPermissions()
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleRemoveAllWebNotificationPermissions(injectedBundle.bundle(), injectedBundle.page()->page());
+}
+
+void TestRunner::simulateWebNotificationClick(JSValueRef notification)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+ uint64_t notificationID = WKBundleGetWebNotificationID(injectedBundle.bundle(), context, notification);
+ injectedBundle.postSimulateWebNotificationClick(notificationID);
+}
+
+void TestRunner::setGeolocationPermission(bool enabled)
+{
+ // FIXME: this should be done by frame.
+ InjectedBundle::singleton().setGeolocationPermission(enabled);
+}
+
+bool TestRunner::isGeolocationProviderActive()
+{
+ return InjectedBundle::singleton().isGeolocationProviderActive();
+}
+
+void TestRunner::setMockGeolocationPosition(double latitude, double longitude, double accuracy, JSValueRef jsAltitude, JSValueRef jsAltitudeAccuracy, JSValueRef jsHeading, JSValueRef jsSpeed)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+
+ bool providesAltitude = false;
+ double altitude = 0.;
+ if (!JSValueIsUndefined(context, jsAltitude)) {
+ providesAltitude = true;
+ altitude = JSValueToNumber(context, jsAltitude, 0);
+ }
+
+ bool providesAltitudeAccuracy = false;
+ double altitudeAccuracy = 0.;
+ if (!JSValueIsUndefined(context, jsAltitudeAccuracy)) {
+ providesAltitudeAccuracy = true;
+ altitudeAccuracy = JSValueToNumber(context, jsAltitudeAccuracy, 0);
+ }
+
+ bool providesHeading = false;
+ double heading = 0.;
+ if (!JSValueIsUndefined(context, jsHeading)) {
+ providesHeading = true;
+ heading = JSValueToNumber(context, jsHeading, 0);
+ }
+
+ bool providesSpeed = false;
+ double speed = 0.;
+ if (!JSValueIsUndefined(context, jsSpeed)) {
+ providesSpeed = true;
+ speed = JSValueToNumber(context, jsSpeed, 0);
+ }
+
+ injectedBundle.setMockGeolocationPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
+}
+
+void TestRunner::setMockGeolocationPositionUnavailableError(JSStringRef message)
+{
+ WKRetainPtr<WKStringRef> messageWK = toWK(message);
+ InjectedBundle::singleton().setMockGeolocationPositionUnavailableError(messageWK.get());
+}
+
+void TestRunner::setUserMediaPermission(bool enabled)
+{
+ // FIXME: this should be done by frame.
+ InjectedBundle::singleton().setUserMediaPermission(enabled);
+}
+
+bool TestRunner::callShouldCloseOnWebView()
+{
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
+ return WKBundleFrameCallShouldCloseOnWebView(mainFrame);
+}
+
+void TestRunner::queueBackNavigation(unsigned howFarBackward)
+{
+ InjectedBundle::singleton().queueBackNavigation(howFarBackward);
+}
+
+void TestRunner::queueForwardNavigation(unsigned howFarForward)
+{
+ InjectedBundle::singleton().queueForwardNavigation(howFarForward);
+}
+
+void TestRunner::queueLoad(JSStringRef url, JSStringRef target, bool shouldOpenExternalURLs)
+{
+ auto& injectedBundle = InjectedBundle::singleton();
+ WKRetainPtr<WKURLRef> baseURLWK(AdoptWK, WKBundleFrameCopyURL(WKBundlePageGetMainFrame(injectedBundle.page()->page())));
+ WKRetainPtr<WKURLRef> urlWK(AdoptWK, WKURLCreateWithBaseURL(baseURLWK.get(), toWTFString(toWK(url)).utf8().data()));
+ WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(urlWK.get()));
+
+ injectedBundle.queueLoad(urlStringWK.get(), toWK(target).get(), shouldOpenExternalURLs);
+}
+
+void TestRunner::queueLoadHTMLString(JSStringRef content, JSStringRef baseURL, JSStringRef unreachableURL)
+{
+ WKRetainPtr<WKStringRef> contentWK = toWK(content);
+ WKRetainPtr<WKStringRef> baseURLWK = baseURL ? toWK(baseURL) : WKRetainPtr<WKStringRef>();
+ WKRetainPtr<WKStringRef> unreachableURLWK = unreachableURL ? toWK(unreachableURL) : WKRetainPtr<WKStringRef>();
+
+ InjectedBundle::singleton().queueLoadHTMLString(contentWK.get(), baseURLWK.get(), unreachableURLWK.get());
+}
+
+void TestRunner::queueReload()
+{
+ InjectedBundle::singleton().queueReload();
+}
+
+void TestRunner::queueLoadingScript(JSStringRef script)
+{
+ WKRetainPtr<WKStringRef> scriptWK = toWK(script);
+ InjectedBundle::singleton().queueLoadingScript(scriptWK.get());
+}
+
+void TestRunner::queueNonLoadingScript(JSStringRef script)
+{
+ WKRetainPtr<WKStringRef> scriptWK = toWK(script);
+ InjectedBundle::singleton().queueNonLoadingScript(scriptWK.get());
+}
+
+void TestRunner::setHandlesAuthenticationChallenges(bool handlesAuthenticationChallenges)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetHandlesAuthenticationChallenge"));
+ WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(handlesAuthenticationChallenges));
+ WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
+}
+
+void TestRunner::setAuthenticationUsername(JSStringRef username)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAuthenticationUsername"));
+ WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(username));
+ WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
+}
+
+void TestRunner::setAuthenticationPassword(JSStringRef password)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAuthenticationPassword"));
+ WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(password));
+ WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
+}
+
+bool TestRunner::secureEventInputIsEnabled() const
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SecureEventInputIsEnabled"));
+ WKTypeRef returnData = 0;
+
+ WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), messageName.get(), 0, &returnData);
+ return WKBooleanGetValue(static_cast<WKBooleanRef>(returnData));
+}
+
+void TestRunner::setBlockAllPlugins(bool shouldBlock)
+{
+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetBlockAllPlugins"));
+ WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(shouldBlock));
+ WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
+}
+
+JSValueRef TestRunner::numberOfDFGCompiles(JSValueRef theFunction)
+{
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+ return JSC::numberOfDFGCompiles(context, theFunction);
+}
+
+JSValueRef TestRunner::neverInlineFunction(JSValueRef theFunction)
+{
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+ return JSC::setNeverInline(context, theFunction);
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
new file mode 100644
index 000000000..41406b316
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2010, 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TestRunner_h
+#define TestRunner_h
+
+#include "JSWrappable.h"
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <WebKit/WKBundleScriptWorld.h>
+#include <WebKit/WKRetainPtr.h>
+#include <string>
+#include <wtf/PassRefPtr.h>
+
+#if PLATFORM(COCOA)
+#include <wtf/RetainPtr.h>
+#include <CoreFoundation/CFRunLoop.h>
+typedef RetainPtr<CFRunLoopTimerRef> PlatformTimerRef;
+#elif PLATFORM(GTK)
+#include <wtf/glib/GMainLoopSource.h>
+typedef GMainLoopSource PlatformTimerRef;
+#elif PLATFORM(EFL)
+typedef Ecore_Timer* PlatformTimerRef;
+#endif
+
+namespace WTR {
+
+class TestRunner : public JSWrappable {
+public:
+ static PassRefPtr<TestRunner> create();
+ virtual ~TestRunner();
+
+ // JSWrappable
+ virtual JSClassRef wrapperClass();
+
+ void makeWindowObject(JSContextRef, JSObjectRef windowObject, JSValueRef* exception);
+
+ // The basics.
+ WKURLRef testURL() const { return m_testURL.get(); }
+ void setTestURL(WKURLRef url) { m_testURL = url; }
+ void dumpAsText(bool dumpPixels);
+ void waitForPolicyDelegate();
+ void dumpChildFramesAsText() { m_whatToDump = AllFramesText; }
+ void waitUntilDone();
+ void notifyDone();
+ double preciseTime();
+
+ // Other dumping.
+ void dumpBackForwardList() { m_shouldDumpBackForwardListsForAllWindows = true; }
+ void dumpChildFrameScrollPositions() { m_shouldDumpAllFrameScrollPositions = true; }
+ void dumpEditingCallbacks() { m_dumpEditingCallbacks = true; }
+ void dumpSelectionRect() { m_dumpSelectionRect = true; }
+ void dumpStatusCallbacks() { m_dumpStatusCallbacks = true; }
+ void dumpTitleChanges() { m_dumpTitleChanges = true; }
+ void dumpFullScreenCallbacks() { m_dumpFullScreenCallbacks = true; }
+ void dumpFrameLoadCallbacks() { setShouldDumpFrameLoadCallbacks(true); }
+ void dumpProgressFinishedCallback() { setShouldDumpProgressFinishedCallback(true); }
+ void dumpResourceLoadCallbacks() { m_dumpResourceLoadCallbacks = true; }
+ void dumpResourceResponseMIMETypes() { m_dumpResourceResponseMIMETypes = true; }
+ void dumpWillCacheResponse() { m_dumpWillCacheResponse = true; }
+ void dumpApplicationCacheDelegateCallbacks() { m_dumpApplicationCacheDelegateCallbacks = true; }
+ void dumpDatabaseCallbacks() { m_dumpDatabaseCallbacks = true; }
+ void dumpDOMAsWebArchive() { m_whatToDump = DOMAsWebArchive; }
+ void dumpPolicyDelegateCallbacks() { m_dumpPolicyCallbacks = true; }
+
+ void setShouldDumpFrameLoadCallbacks(bool value) { m_dumpFrameLoadCallbacks = value; }
+ void setShouldDumpProgressFinishedCallback(bool value) { m_dumpProgressFinishedCallback = value; }
+
+ // Special options.
+ void keepWebHistory();
+ void setAcceptsEditing(bool value) { m_shouldAllowEditing = value; }
+ void setCanOpenWindows(bool);
+ void setCloseRemainingWindowsWhenComplete(bool value) { m_shouldCloseExtraWindows = value; }
+ void setXSSAuditorEnabled(bool);
+ void setAllowUniversalAccessFromFileURLs(bool);
+ void setAllowFileAccessFromFileURLs(bool);
+ void setPluginsEnabled(bool);
+ void setJavaScriptCanAccessClipboard(bool);
+ void setPrivateBrowsingEnabled(bool);
+ void setPopupBlockingEnabled(bool);
+ void setAuthorAndUserStylesEnabled(bool);
+ void setCustomPolicyDelegate(bool enabled, bool permissive = false);
+ void addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains);
+ void removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains);
+ void setUserStyleSheetEnabled(bool);
+ void setUserStyleSheetLocation(JSStringRef);
+ void setSpatialNavigationEnabled(bool);
+ void setTabKeyCyclesThroughElements(bool);
+ void setSerializeHTTPLoads();
+ void dispatchPendingLoadRequests();
+ void setCacheModel(int);
+ void setAsynchronousSpellCheckingEnabled(bool);
+
+ // Special DOM functions.
+ void clearBackForwardList();
+ void execCommand(JSStringRef name, JSStringRef argument);
+ bool isCommandEnabled(JSStringRef name);
+ unsigned windowCount();
+
+ // Repaint testing.
+ void testRepaint() { m_testRepaint = true; }
+ void repaintSweepHorizontally() { m_testRepaintSweepHorizontally = true; }
+ void display();
+
+ // UserContent testing.
+ void addUserScript(JSStringRef source, bool runAtStart, bool allFrames);
+ void addUserStyleSheet(JSStringRef source, bool allFrames);
+
+ // Text search testing.
+ bool findString(JSStringRef, JSValueRef optionsArray);
+
+ // Local storage
+ void clearAllDatabases();
+ void setDatabaseQuota(uint64_t);
+ JSRetainPtr<JSStringRef> pathToLocalResource(JSStringRef);
+
+ // Application Cache
+ void clearAllApplicationCaches();
+ void clearApplicationCacheForOrigin(JSStringRef origin);
+ void setAppCacheMaximumSize(uint64_t);
+ long long applicationCacheDiskUsageForOrigin(JSStringRef origin);
+ void disallowIncreaseForApplicationCacheQuota();
+ bool shouldDisallowIncreaseForApplicationCacheQuota() { return m_disallowIncreaseForApplicationCacheQuota; }
+ JSValueRef originsWithApplicationCache();
+
+ // Printing
+ bool isPageBoxVisible(int pageIndex);
+ bool isPrinting() { return m_isPrinting; }
+ void setPrinting() { m_isPrinting = true; }
+
+ // Authentication
+ void setHandlesAuthenticationChallenges(bool);
+ void setAuthenticationUsername(JSStringRef);
+ void setAuthenticationPassword(JSStringRef);
+
+ void setValueForUser(JSContextRef, JSValueRef element, JSStringRef value);
+
+ // Audio testing.
+ void setAudioResult(JSContextRef, JSValueRef data);
+
+ void setBlockAllPlugins(bool shouldBlock);
+
+ enum WhatToDump { RenderTree, MainFrameText, AllFramesText, Audio, DOMAsWebArchive };
+ WhatToDump whatToDump() const { return m_whatToDump; }
+
+ bool shouldDumpAllFrameScrollPositions() const { return m_shouldDumpAllFrameScrollPositions; }
+ bool shouldDumpBackForwardListsForAllWindows() const { return m_shouldDumpBackForwardListsForAllWindows; }
+ bool shouldDumpEditingCallbacks() const { return m_dumpEditingCallbacks; }
+ bool shouldDumpMainFrameScrollPosition() const { return m_whatToDump == RenderTree; }
+ bool shouldDumpStatusCallbacks() const { return m_dumpStatusCallbacks; }
+ bool shouldDumpTitleChanges() const { return m_dumpTitleChanges; }
+ bool shouldDumpPixels() const { return m_dumpPixels; }
+ bool shouldDumpFullScreenCallbacks() const { return m_dumpFullScreenCallbacks; }
+ bool shouldDumpFrameLoadCallbacks() const { return m_dumpFrameLoadCallbacks; }
+ bool shouldDumpProgressFinishedCallback() const { return m_dumpProgressFinishedCallback; }
+ bool shouldDumpResourceLoadCallbacks() const { return m_dumpResourceLoadCallbacks; }
+ bool shouldDumpResourceResponseMIMETypes() const { return m_dumpResourceResponseMIMETypes; }
+ bool shouldDumpWillCacheResponse() const { return m_dumpWillCacheResponse; }
+ bool shouldDumpApplicationCacheDelegateCallbacks() const { return m_dumpApplicationCacheDelegateCallbacks; }
+ bool shouldDumpDatabaseCallbacks() const { return m_dumpDatabaseCallbacks; }
+ bool shouldDumpSelectionRect() const { return m_dumpSelectionRect; }
+ bool shouldDumpPolicyCallbacks() const { return m_dumpPolicyCallbacks; }
+
+ bool isPolicyDelegateEnabled() const { return m_policyDelegateEnabled; }
+ bool isPolicyDelegatePermissive() const { return m_policyDelegatePermissive; }
+
+ bool waitToDump() const { return m_waitToDump; }
+ void waitToDumpWatchdogTimerFired();
+ void invalidateWaitToDumpWatchdogTimer();
+
+ bool shouldAllowEditing() const { return m_shouldAllowEditing; }
+
+ bool shouldCloseExtraWindowsAfterRunningTest() const { return m_shouldCloseExtraWindows; }
+
+ void evaluateScriptInIsolatedWorld(JSContextRef, unsigned worldID, JSStringRef script);
+ static unsigned worldIDForWorld(WKBundleScriptWorldRef);
+
+ void showWebInspector();
+ void closeWebInspector();
+ void evaluateInWebInspector(JSStringRef script);
+
+ void setPOSIXLocale(JSStringRef);
+
+ bool willSendRequestReturnsNull() const { return m_willSendRequestReturnsNull; }
+ void setWillSendRequestReturnsNull(bool f) { m_willSendRequestReturnsNull = f; }
+ bool willSendRequestReturnsNullOnRedirect() const { return m_willSendRequestReturnsNullOnRedirect; }
+ void setWillSendRequestReturnsNullOnRedirect(bool f) { m_willSendRequestReturnsNullOnRedirect = f; }
+
+ void setTextDirection(JSStringRef);
+
+ void setShouldStayOnPageAfterHandlingBeforeUnload(bool);
+
+ void setDefersLoading(bool);
+
+ void setStopProvisionalFrameLoads() { m_shouldStopProvisionalFrameLoads = true; }
+ bool shouldStopProvisionalFrameLoads() const { return m_shouldStopProvisionalFrameLoads; }
+
+ bool globalFlag() const { return m_globalFlag; }
+ void setGlobalFlag(bool value) { m_globalFlag = value; }
+
+ double databaseDefaultQuota() const { return m_databaseDefaultQuota; }
+ void setDatabaseDefaultQuota(double quota) { m_databaseDefaultQuota = quota; }
+
+ double databaseMaxQuota() const { return m_databaseMaxQuota; }
+ void setDatabaseMaxQuota(double quota) { m_databaseMaxQuota = quota; }
+
+ void addChromeInputField(JSValueRef);
+ void removeChromeInputField(JSValueRef);
+ void focusWebView(JSValueRef);
+ void setBackingScaleFactor(double, JSValueRef);
+
+ void setWindowIsKey(bool);
+
+ void callAddChromeInputFieldCallback();
+ void callRemoveChromeInputFieldCallback();
+ void callFocusWebViewCallback();
+ void callSetBackingScaleFactorCallback();
+
+ void overridePreference(JSStringRef preference, JSStringRef value);
+
+ // Cookies testing
+ void setAlwaysAcceptCookies(bool);
+
+ // Custom full screen behavior.
+ void setHasCustomFullScreenBehavior(bool value) { m_customFullScreenBehavior = value; }
+ bool hasCustomFullScreenBehavior() const { return m_customFullScreenBehavior; }
+
+ // Web notifications.
+ void grantWebNotificationPermission(JSStringRef origin);
+ void denyWebNotificationPermission(JSStringRef origin);
+ void removeAllWebNotificationPermissions();
+ void simulateWebNotificationClick(JSValueRef notification);
+
+ // Geolocation.
+ void setGeolocationPermission(bool);
+ void setMockGeolocationPosition(double latitude, double longitude, double accuracy, JSValueRef altitude, JSValueRef altitudeAccuracy, JSValueRef heading, JSValueRef speed);
+ void setMockGeolocationPositionUnavailableError(JSStringRef message);
+ bool isGeolocationProviderActive();
+
+ // MediaStream
+ void setUserMediaPermission(bool);
+
+ void setPageVisibility(JSStringRef state);
+ void resetPageVisibility();
+
+ bool callShouldCloseOnWebView();
+
+ void setCustomTimeout(int duration) { m_timeout = duration; }
+
+ // Work queue.
+ void queueBackNavigation(unsigned howFarBackward);
+ void queueForwardNavigation(unsigned howFarForward);
+ void queueLoad(JSStringRef url, JSStringRef target, bool shouldOpenExternalURLs);
+ void queueLoadHTMLString(JSStringRef content, JSStringRef baseURL, JSStringRef unreachableURL);
+ void queueReload();
+ void queueLoadingScript(JSStringRef script);
+ void queueNonLoadingScript(JSStringRef script);
+
+ bool secureEventInputIsEnabled() const;
+
+ JSValueRef numberOfDFGCompiles(JSValueRef theFunction);
+ JSValueRef neverInlineFunction(JSValueRef theFunction);
+
+private:
+ TestRunner();
+
+ void platformInitialize();
+ void initializeWaitToDumpWatchdogTimerIfNeeded();
+
+ WKRetainPtr<WKURLRef> m_testURL; // Set by InjectedBundlePage once provisional load starts.
+
+ WhatToDump m_whatToDump;
+ bool m_shouldDumpAllFrameScrollPositions;
+ bool m_shouldDumpBackForwardListsForAllWindows;
+
+ bool m_shouldAllowEditing;
+ bool m_shouldCloseExtraWindows;
+
+ bool m_dumpEditingCallbacks;
+ bool m_dumpStatusCallbacks;
+ bool m_dumpTitleChanges;
+ bool m_dumpPixels;
+ bool m_dumpSelectionRect;
+ bool m_dumpFullScreenCallbacks;
+ bool m_dumpFrameLoadCallbacks;
+ bool m_dumpProgressFinishedCallback;
+ bool m_dumpResourceLoadCallbacks;
+ bool m_dumpResourceResponseMIMETypes;
+ bool m_dumpWillCacheResponse;
+ bool m_dumpApplicationCacheDelegateCallbacks;
+ bool m_dumpDatabaseCallbacks;
+ bool m_dumpPolicyCallbacks { false };
+ bool m_disallowIncreaseForApplicationCacheQuota;
+ bool m_waitToDump; // True if waitUntilDone() has been called, but notifyDone() has not yet been called.
+ bool m_testRepaint;
+ bool m_testRepaintSweepHorizontally;
+ bool m_isPrinting;
+
+ bool m_willSendRequestReturnsNull;
+ bool m_willSendRequestReturnsNullOnRedirect;
+ bool m_shouldStopProvisionalFrameLoads;
+
+ bool m_policyDelegateEnabled;
+ bool m_policyDelegatePermissive;
+
+ bool m_globalFlag;
+ bool m_customFullScreenBehavior;
+
+ int m_timeout;
+
+ double m_databaseDefaultQuota;
+ double m_databaseMaxQuota;
+
+ bool m_userStyleSheetEnabled;
+ WKRetainPtr<WKStringRef> m_userStyleSheetLocation;
+
+ WKRetainPtr<WKArrayRef> m_allowedHosts;
+
+ PlatformTimerRef m_waitToDumpWatchdogTimer;
+};
+
+} // namespace WTR
+
+#endif // TestRunner_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/TextInputController.cpp b/Tools/WebKitTestRunner/InjectedBundle/TextInputController.cpp
new file mode 100644
index 000000000..f4e68f695
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/TextInputController.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TextInputController.h"
+
+#include "InjectedBundle.h"
+#include "InjectedBundlePage.h"
+#include "JSTextInputController.h"
+#include "StringFunctions.h"
+#include <WebKit/WKBundlePagePrivate.h>
+
+namespace WTR {
+
+PassRefPtr<TextInputController> TextInputController::create()
+{
+ return adoptRef(new TextInputController);
+}
+
+TextInputController::TextInputController()
+{
+}
+
+TextInputController::~TextInputController()
+{
+}
+
+JSClassRef TextInputController::wrapperClass()
+{
+ return JSTextInputController::textInputControllerClass();
+}
+
+void TextInputController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
+{
+ setProperty(context, windowObject, "textInputController", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
+}
+
+void TextInputController::setMarkedText(JSStringRef text, int from, int length)
+{
+ WKBundlePageSetComposition(InjectedBundle::singleton().page()->page(), toWK(text).get(), from, length);
+}
+
+bool TextInputController::hasMarkedText()
+{
+ return WKBundlePageHasComposition(InjectedBundle::singleton().page()->page());
+}
+
+void TextInputController::unmarkText()
+{
+ WKBundlePageConfirmComposition(InjectedBundle::singleton().page()->page());
+}
+
+void TextInputController::insertText(JSStringRef text)
+{
+ WKBundlePageConfirmCompositionWithText(InjectedBundle::singleton().page()->page(), toWK(text).get());
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/InjectedBundle/TextInputController.h b/Tools/WebKitTestRunner/InjectedBundle/TextInputController.h
new file mode 100644
index 000000000..71a043752
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/TextInputController.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TextInputController_h
+#define TextInputController_h
+
+#include "JSWrappable.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WTR {
+
+class TextInputController : public JSWrappable {
+public:
+ static PassRefPtr<TextInputController> create();
+ virtual ~TextInputController();
+
+ // JSWrappable
+ virtual JSClassRef wrapperClass();
+
+ void makeWindowObject(JSContextRef, JSObjectRef windowObject, JSValueRef* exception);
+
+ void setMarkedText(JSStringRef text, int from, int length);
+ bool hasMarkedText();
+ void unmarkText();
+ void insertText(JSStringRef text);
+
+private:
+ TextInputController();
+};
+
+} // namespace WTR
+
+#endif // TextInputController_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityControllerAtk.cpp b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityControllerAtk.cpp
new file mode 100644
index 000000000..c1cb34951
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityControllerAtk.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AccessibilityController.h"
+
+#if HAVE(ACCESSIBILITY)
+
+#include "InjectedBundle.h"
+#include "InjectedBundlePage.h"
+
+#include <WebKit/WKBundlePagePrivate.h>
+#include <atk/atk.h>
+#include <cstdio>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WTR {
+
+void AccessibilityController::logAccessibilityEvents()
+{
+ // No longer implemented for ATK. Use addNotificationListener() instead to
+ // check that relevant ATK signals are being emmitted in response to events.
+}
+
+void AccessibilityController::resetToConsistentState()
+{
+ m_globalNotificationHandler = nullptr;
+}
+
+static AtkObject* childElementById(AtkObject* parent, const char* id)
+{
+ if (!ATK_IS_OBJECT(parent))
+ return nullptr;
+
+ bool parentFound = false;
+ AtkAttributeSet* attributeSet = atk_object_get_attributes(parent);
+ for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
+ AtkAttribute* attribute = static_cast<AtkAttribute*>(attributes->data);
+ if (!strcmp(attribute->name, "html-id")) {
+ if (!strcmp(attribute->value, id))
+ parentFound = true;
+ break;
+ }
+ }
+ atk_attribute_set_free(attributeSet);
+
+ if (parentFound)
+ return parent;
+
+ int childCount = atk_object_get_n_accessible_children(parent);
+ for (int i = 0; i < childCount; i++) {
+ AtkObject* result = childElementById(atk_object_ref_accessible_child(parent, i), id);
+ if (ATK_IS_OBJECT(result))
+ return result;
+ }
+
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityController::accessibleElementById(JSStringRef id)
+{
+ AtkObject* root = ATK_OBJECT(WKAccessibilityRootObject(InjectedBundle::singleton().page()->page()));
+ if (!root)
+ return nullptr;
+
+ size_t bufferSize = JSStringGetMaximumUTF8CStringSize(id);
+ GUniquePtr<gchar> idBuffer(static_cast<gchar*>(g_malloc(bufferSize)));
+ JSStringGetUTF8CString(id, idBuffer.get(), bufferSize);
+
+ AtkObject* result = childElementById(root, idBuffer.get());
+ if (ATK_IS_OBJECT(result))
+ return AccessibilityUIElement::create(result);
+
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityController::platformName()
+{
+ JSRetainPtr<JSStringRef> platformName(Adopt, JSStringCreateWithUTF8CString("atk"));
+ return platformName;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityController::rootElement()
+{
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ void* root = WKAccessibilityRootObject(page);
+
+ return AccessibilityUIElement::create(static_cast<AtkObject*>(root));
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityController::focusedElement()
+{
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ void* root = WKAccessibilityFocusedObject(page);
+
+ return AccessibilityUIElement::create(static_cast<AtkObject*>(root));
+}
+
+bool AccessibilityController::addNotificationListener(JSValueRef functionCallback)
+{
+ if (!functionCallback)
+ return false;
+
+ // Only one global notification listener.
+ if (!m_globalNotificationHandler)
+ m_globalNotificationHandler = AccessibilityNotificationHandler::create();
+ m_globalNotificationHandler->setNotificationFunctionCallback(functionCallback);
+
+ return true;
+}
+
+bool AccessibilityController::removeNotificationListener()
+{
+ // Programmers should not be trying to remove a listener that's already removed.
+ ASSERT(m_globalNotificationHandler);
+
+ m_globalNotificationHandler = nullptr;
+ return false;
+}
+
+} // namespace WTR
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp
new file mode 100644
index 000000000..95cf8ae4b
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "AccessibilityNotificationHandlerAtk.h"
+
+#if HAVE(ACCESSIBILITY)
+
+#include "InjectedBundle.h"
+#include "InjectedBundlePage.h"
+#include "JSWrapper.h"
+#include <WebKit/WKBundleFrame.h>
+#include <WebKit/WKBundlePage.h>
+#include <WebKit/WKBundlePagePrivate.h>
+#include <wtf/HashMap.h>
+#include <wtf/Vector.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+namespace WTR {
+
+namespace {
+
+typedef HashMap<AtkObject*, AccessibilityNotificationHandler*> NotificationHandlersMap;
+
+WTF::Vector<unsigned> listenerIds;
+NotificationHandlersMap notificationHandlers;
+AccessibilityNotificationHandler* globalNotificationHandler = nullptr;
+
+gboolean axObjectEventListener(GSignalInvocationHint* signalHint, unsigned numParamValues, const GValue* paramValues, gpointer data)
+{
+ // At least we should receive the instance emitting the signal.
+ if (!numParamValues)
+ return true;
+
+ AtkObject* accessible = ATK_OBJECT(g_value_get_object(&paramValues[0]));
+ if (!accessible || !ATK_IS_OBJECT(accessible))
+ return true;
+
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(page);
+ JSContextRef jsContext = WKBundleFrameGetJavaScriptContext(mainFrame);
+#else
+ JSContextRef jsContext = nullptr;
+#endif
+
+ GSignalQuery signalQuery;
+ const char* notificationName = nullptr;
+ Vector<JSValueRef> extraArgs;
+
+ g_signal_query(signalHint->signal_id, &signalQuery);
+
+ if (!g_strcmp0(signalQuery.signal_name, "state-change")) {
+ if (!g_strcmp0(g_value_get_string(&paramValues[1]), "checked"))
+ notificationName = "CheckedStateChanged";
+ else if (!g_strcmp0(g_value_get_string(&paramValues[1]), "invalid-entry"))
+ notificationName = "AXInvalidStatusChanged";
+ } else if (!g_strcmp0(signalQuery.signal_name, "focus-event")) {
+ if (g_value_get_boolean(&paramValues[1]))
+ notificationName = "AXFocusedUIElementChanged";
+ } else if (!g_strcmp0(signalQuery.signal_name, "selection-changed")) {
+ notificationName = "AXSelectedChildrenChanged";
+ } else if (!g_strcmp0(signalQuery.signal_name, "children-changed")) {
+ const gchar* childrenChangedDetail = g_quark_to_string(signalHint->detail);
+ notificationName = !g_strcmp0(childrenChangedDetail, "add") ? "AXChildrenAdded" : "AXChildrenRemoved";
+ } else if (!g_strcmp0(signalQuery.signal_name, "property-change")) {
+ if (!g_strcmp0(g_quark_to_string(signalHint->detail), "accessible-value"))
+ notificationName = "AXValueChanged";
+ } else if (!g_strcmp0(signalQuery.signal_name, "load-complete"))
+ notificationName = "AXLoadComplete";
+ else if (!g_strcmp0(signalQuery.signal_name, "text-caret-moved")) {
+ notificationName = "AXTextCaretMoved";
+ GUniquePtr<char> signalValue(g_strdup_printf("%d", g_value_get_int(&paramValues[1])));
+ JSRetainPtr<JSStringRef> jsSignalValue(Adopt, JSStringCreateWithUTF8CString(signalValue.get()));
+ extraArgs.append(JSValueMakeString(jsContext, jsSignalValue.get()));
+ }
+
+ if (!jsContext)
+ return true;
+
+ if (notificationName) {
+ JSRetainPtr<JSStringRef> jsNotificationEventName(Adopt, JSStringCreateWithUTF8CString(notificationName));
+ JSValueRef notificationNameArgument = JSValueMakeString(jsContext, jsNotificationEventName.get());
+ NotificationHandlersMap::iterator elementNotificationHandler = notificationHandlers.find(accessible);
+ JSValueRef arguments[5]; // this dimension must be >= 2 + max(extraArgs.size())
+ arguments[0] = toJS(jsContext, WTF::getPtr(WTR::AccessibilityUIElement::create(accessible)));
+ arguments[1] = notificationNameArgument;
+ size_t numOfExtraArgs = extraArgs.size();
+ for (int i = 0; i < numOfExtraArgs; i++)
+ arguments[i + 2] = extraArgs[i];
+ if (elementNotificationHandler != notificationHandlers.end()) {
+ // Listener for one element. As arguments, it gets the notification name
+ // and sometimes extra arguments.
+ JSObjectCallAsFunction(jsContext,
+ const_cast<JSObjectRef>(elementNotificationHandler->value->notificationFunctionCallback()),
+ 0, numOfExtraArgs + 1, arguments + 1, 0);
+ }
+
+ if (globalNotificationHandler) {
+ // A global listener gets additionally the element as the first argument.
+ JSObjectCallAsFunction(jsContext,
+ const_cast<JSObjectRef>(globalNotificationHandler->notificationFunctionCallback()),
+ 0, numOfExtraArgs + 2, arguments, 0);
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+AccessibilityNotificationHandler::AccessibilityNotificationHandler()
+ : m_platformElement(0)
+ , m_notificationFunctionCallback(0)
+{
+}
+
+AccessibilityNotificationHandler::~AccessibilityNotificationHandler()
+{
+ removeAccessibilityNotificationHandler();
+ disconnectAccessibilityCallbacks();
+}
+
+void AccessibilityNotificationHandler::setNotificationFunctionCallback(JSValueRef notificationFunctionCallback)
+{
+ if (!notificationFunctionCallback) {
+ removeAccessibilityNotificationHandler();
+ disconnectAccessibilityCallbacks();
+ return;
+ }
+
+ m_notificationFunctionCallback = notificationFunctionCallback;
+
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(page);
+ JSContextRef jsContext = WKBundleFrameGetJavaScriptContext(mainFrame);
+#else
+ JSContextRef jsContext = nullptr;
+#endif
+ if (!jsContext)
+ return;
+
+ connectAccessibilityCallbacks();
+
+ JSValueProtect(jsContext, m_notificationFunctionCallback);
+ // Check if this notification handler is related to a specific element.
+ if (m_platformElement) {
+ NotificationHandlersMap::iterator currentNotificationHandler = notificationHandlers.find(m_platformElement.get());
+ if (currentNotificationHandler != notificationHandlers.end()) {
+ ASSERT(currentNotificationHandler->value->platformElement());
+ JSValueUnprotect(jsContext, currentNotificationHandler->value->notificationFunctionCallback());
+ notificationHandlers.remove(currentNotificationHandler->value->platformElement().get());
+ }
+ notificationHandlers.add(m_platformElement.get(), this);
+ } else {
+ if (globalNotificationHandler)
+ JSValueUnprotect(jsContext, globalNotificationHandler->notificationFunctionCallback());
+ globalNotificationHandler = this;
+ }
+}
+
+void AccessibilityNotificationHandler::removeAccessibilityNotificationHandler()
+{
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(page);
+ JSContextRef jsContext = WKBundleFrameGetJavaScriptContext(mainFrame);
+#else
+ JSContextRef jsContext = nullptr;
+#endif
+ if (!jsContext)
+ return;
+
+ if (globalNotificationHandler == this) {
+ JSValueUnprotect(jsContext, globalNotificationHandler->notificationFunctionCallback());
+ globalNotificationHandler = nullptr;
+ } else if (m_platformElement.get()) {
+ NotificationHandlersMap::iterator removeNotificationHandler = notificationHandlers.find(m_platformElement.get());
+ if (removeNotificationHandler != notificationHandlers.end()) {
+ JSValueUnprotect(jsContext, removeNotificationHandler->value->notificationFunctionCallback());
+ notificationHandlers.remove(removeNotificationHandler);
+ }
+ }
+}
+
+void AccessibilityNotificationHandler::connectAccessibilityCallbacks()
+{
+ // Ensure no callbacks are connected before.
+ if (!disconnectAccessibilityCallbacks())
+ return;
+
+ const char* signalNames[] = {
+ "ATK:AtkObject:state-change",
+ "ATK:AtkObject:focus-event",
+ "ATK:AtkObject:active-descendant-changed",
+ "ATK:AtkObject:children-changed",
+ "ATK:AtkObject:property-change",
+ "ATK:AtkObject:visible-data-changed",
+ "ATK:AtkDocument:load-complete",
+ "ATK:AtkSelection:selection-changed",
+ "ATK:AtkText:text-caret-moved",
+ 0
+ };
+
+ // Register atk interfaces, otherwise add_global may fail.
+ GObject* dummyObject = (GObject*)g_object_new(G_TYPE_OBJECT, NULL, NULL);
+ g_object_unref(atk_no_op_object_new(dummyObject));
+ g_object_unref(dummyObject);
+
+ // Add global listeners for AtkObject's signals.
+ for (const char** signalName = signalNames; *signalName; signalName++) {
+ unsigned id = atk_add_global_event_listener(axObjectEventListener, *signalName);
+ if (!id) {
+ String message = String::format("atk_add_global_event_listener failed for signal %s\n", *signalName);
+ InjectedBundle::singleton().outputText(message);
+ continue;
+ }
+
+ listenerIds.append(id);
+ }
+}
+
+bool AccessibilityNotificationHandler::disconnectAccessibilityCallbacks()
+{
+ // Only disconnect if there is no notification handler.
+ if (!notificationHandlers.isEmpty() || globalNotificationHandler)
+ return false;
+
+ // AtkObject signals.
+ for (int i = 0; i < listenerIds.size(); i++) {
+ ASSERT(listenerIds[i]);
+ atk_remove_global_event_listener(listenerIds[i]);
+ }
+ listenerIds.clear();
+ return true;
+}
+
+} // namespace WTR
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.h b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.h
new file mode 100644
index 000000000..bb3869ecd
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef AccessibilityNotificationHandlerAtk_h
+#define AccessibilityNotificationHandlerAtk_h
+
+#if HAVE(ACCESSIBILITY)
+
+#include <JavaScriptCore/JSObjectRef.h>
+#include <atk/atk.h>
+#include <atk/atkobject.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/glib/GRefPtr.h>
+
+namespace WTR {
+
+class AccessibilityNotificationHandler : public RefCounted<AccessibilityNotificationHandler> {
+public:
+ static PassRefPtr<AccessibilityNotificationHandler> create()
+ {
+ return adoptRef(new AccessibilityNotificationHandler());
+ }
+ ~AccessibilityNotificationHandler();
+ void setPlatformElement(GRefPtr<AtkObject> platformElement) { m_platformElement = platformElement; }
+ GRefPtr<AtkObject> platformElement() const { return m_platformElement; }
+ void setNotificationFunctionCallback(JSValueRef);
+ JSValueRef notificationFunctionCallback() const { return m_notificationFunctionCallback; }
+
+private:
+ AccessibilityNotificationHandler();
+ void connectAccessibilityCallbacks();
+ bool disconnectAccessibilityCallbacks();
+ void removeAccessibilityNotificationHandler();
+
+ GRefPtr<AtkObject> m_platformElement;
+ JSValueRef m_notificationFunctionCallback;
+};
+
+} // namespace WTR
+
+#endif // HAVE(ACCESSIBILITY)
+
+#endif // AccessibilityNotificationHandlerAtk_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp
new file mode 100644
index 000000000..29cd3a848
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp
@@ -0,0 +1,2026 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2012 Igalia S.L.
+ * Copyright (C) 2013 Samsung Electronics. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AccessibilityUIElement.h"
+
+#if HAVE(ACCESSIBILITY)
+
+#include "InjectedBundle.h"
+#include "InjectedBundlePage.h"
+#include "NotImplemented.h"
+#include <JavaScriptCore/JSStringRef.h>
+#include <JavaScriptCore/OpaqueJSString.h>
+#if ATK_CHECK_VERSION(2,11,90)
+#include <WebKit/WKBundleFrame.h>
+#endif
+#include <atk/atk.h>
+#include <wtf/Assertions.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/WTFString.h>
+#include <wtf/unicode/CharacterNames.h>
+
+namespace WTR {
+
+namespace {
+
+#if ATK_CHECK_VERSION(2,11,92)
+enum RangeLimit {
+ RangeLimitMinimum,
+ RangeLimitMaximum
+};
+#endif
+
+enum AtkAttributeType {
+ ObjectAttributeType,
+ TextAttributeType
+};
+
+enum AttributeDomain {
+ CoreDomain = 0,
+ AtkDomain
+};
+
+enum AttributesIndex {
+ // Attribute names.
+ InvalidNameIndex = 0,
+ PosInSetIndex,
+ SetSizeIndex,
+ PlaceholderNameIndex,
+ SortNameIndex,
+
+ // Attribute values.
+ SortAscendingValueIndex,
+ SortDescendingValueIndex,
+ SortUnknownValueIndex,
+
+ NumberOfAttributes
+};
+
+// Attribute names & Values (keep on sync with enum AttributesIndex).
+const String attributesMap[][2] = {
+ // Attribute names.
+ { "AXInvalid", "invalid" },
+ { "AXARIAPosInSet", "posinset" },
+ { "AXARIASetSize", "setsize" },
+ { "AXPlaceholderValue", "placeholder-text" } ,
+ { "AXSortDirection", "sort" },
+
+ // Attribute values.
+ { "AXAscendingSortDirection", "ascending" },
+ { "AXDescendingSortDirection", "descending" },
+ { "AXUnknownSortDirection", "unknown" }
+};
+
+#if ATK_CHECK_VERSION(2, 11, 3)
+const char* landmarkStringBanner = "AXLandmarkBanner";
+const char* landmarkStringComplementary = "AXLandmarkComplementary";
+const char* landmarkStringContentinfo = "AXLandmarkContentInfo";
+const char* landmarkStringMain = "AXLandmarkMain";
+const char* landmarkStringNavigation = "AXLandmarkNavigation";
+const char* landmarkStringSearch = "AXLandmarkSearch";
+#endif
+
+String jsStringToWTFString(JSStringRef attribute)
+{
+ size_t bufferSize = JSStringGetMaximumUTF8CStringSize(attribute);
+ GUniquePtr<gchar> buffer(static_cast<gchar*>(g_malloc(bufferSize)));
+ JSStringGetUTF8CString(attribute, buffer.get(), bufferSize);
+
+ return String::fromUTF8(buffer.get());
+}
+
+String coreAttributeToAtkAttribute(JSStringRef attribute)
+{
+ String attributeString = jsStringToWTFString(attribute);
+ for (int i = 0; i < NumberOfAttributes; ++i) {
+ if (attributesMap[i][CoreDomain] == attributeString)
+ return attributesMap[i][AtkDomain];
+ }
+
+ return attributeString;
+}
+
+String atkAttributeValueToCoreAttributeValue(AtkAttributeType type, const String& id, const String& value)
+{
+ if (type == ObjectAttributeType) {
+ // We need to translate ATK values exposed for 'aria-sort' (e.g. 'ascending')
+ // into those expected by the layout tests (e.g. 'AXAscendingSortDirection').
+ if (id == attributesMap[SortNameIndex][AtkDomain] && !value.isEmpty()) {
+ if (value == attributesMap[SortAscendingValueIndex][AtkDomain])
+ return attributesMap[SortAscendingValueIndex][CoreDomain];
+ if (value == attributesMap[SortDescendingValueIndex][AtkDomain])
+ return attributesMap[SortDescendingValueIndex][CoreDomain];
+
+ return attributesMap[SortUnknownValueIndex][CoreDomain];
+ }
+ } else if (type == TextAttributeType) {
+ // In case of 'aria-invalid' when the attribute empty or has "false" for ATK
+ // it should not be mapped at all, but layout tests will expect 'false'.
+ if (id == attributesMap[InvalidNameIndex][AtkDomain] && value.isEmpty())
+ return "false";
+ }
+
+ return value;
+}
+
+AtkAttributeSet* getAttributeSet(AtkObject* accessible, AtkAttributeType type)
+{
+ if (type == ObjectAttributeType)
+ return atk_object_get_attributes(accessible);
+
+ if (type == TextAttributeType) {
+ if (!ATK_IS_TEXT(accessible))
+ return nullptr;
+
+ return atk_text_get_default_attributes(ATK_TEXT(accessible));
+ }
+
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+String getAttributeSetValueForId(AtkObject* accessible, AtkAttributeType type, String id)
+{
+ AtkAttributeSet* attributeSet = getAttributeSet(accessible, type);
+ if (!attributeSet)
+ return String();
+
+ String attributeValue;
+ for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
+ AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
+ if (id == atkAttribute->name) {
+ attributeValue = String::fromUTF8(atkAttribute->value);
+ break;
+ }
+ }
+ atk_attribute_set_free(attributeSet);
+
+ return atkAttributeValueToCoreAttributeValue(type, id, attributeValue);
+}
+
+String getAtkAttributeSetAsString(AtkObject* accessible, AtkAttributeType type)
+{
+ AtkAttributeSet* attributeSet = getAttributeSet(accessible, type);
+ if (!attributeSet)
+ return String();
+
+ StringBuilder builder;
+ for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
+ AtkAttribute* attribute = static_cast<AtkAttribute*>(attributes->data);
+ GUniquePtr<gchar> attributeData(g_strconcat(attribute->name, ":", attribute->value, NULL));
+ builder.append(attributeData.get());
+ if (attributes->next)
+ builder.append(", ");
+ }
+ atk_attribute_set_free(attributeSet);
+
+ return builder.toString();
+}
+
+bool checkElementState(PlatformUIElement element, AtkStateType stateType)
+{
+ if (!ATK_IS_OBJECT(element.get()))
+ return false;
+
+ GRefPtr<AtkStateSet> stateSet = adoptGRef(atk_object_ref_state_set(ATK_OBJECT(element.get())));
+ return atk_state_set_contains_state(stateSet.get(), stateType);
+}
+
+JSStringRef indexRangeInTable(PlatformUIElement element, bool isRowRange)
+{
+ GUniquePtr<gchar> rangeString(g_strdup("{0, 0}"));
+#if ATK_CHECK_VERSION(2,11,90)
+ if (!ATK_IS_TABLE_CELL(element.get()))
+ return JSStringCreateWithUTF8CString(rangeString.get());
+#else
+ if (!ATK_IS_OBJECT(element.get()))
+ return JSStringCreateWithUTF8CString(rangeString.get());
+
+ AtkObject* axTable = atk_object_get_parent(ATK_OBJECT(element.get()));
+ if (!axTable || !ATK_IS_TABLE(axTable))
+ return JSStringCreateWithUTF8CString(rangeString.get());
+
+ // Look for the cell in the table.
+ gint indexInParent = atk_object_get_index_in_parent(ATK_OBJECT(element.get()));
+ if (indexInParent == -1)
+ return JSStringCreateWithUTF8CString(rangeString.get());
+#endif
+
+ gint row = -1;
+ gint column = -1;
+ gint rowSpan = -1;
+ gint columnSpan = -1;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_table_cell_get_row_column_span(ATK_TABLE_CELL(element.get()), &row, &column, &rowSpan, &columnSpan);
+#else
+ row = atk_table_get_row_at_index(ATK_TABLE(axTable), indexInParent);
+ column = atk_table_get_column_at_index(ATK_TABLE(axTable), indexInParent);
+ rowSpan = atk_table_get_row_extent_at(ATK_TABLE(axTable), row, column);
+ columnSpan = atk_table_get_column_extent_at(ATK_TABLE(axTable), row, column);
+#endif
+
+ // Get the actual values, if row and columns are valid values.
+ if (row != -1 && column != -1) {
+ int base = 0;
+ int length = 0;
+ if (isRowRange) {
+ base = row;
+ length = rowSpan;
+ } else {
+ base = column;
+ length = columnSpan;
+ }
+ rangeString.reset(g_strdup_printf("{%d, %d}", base, length));
+ }
+
+ return JSStringCreateWithUTF8CString(rangeString.get());
+}
+
+void alterCurrentValue(PlatformUIElement element, int factor)
+{
+ if (!ATK_IS_VALUE(element.get()))
+ return;
+
+#if ATK_CHECK_VERSION(2,11,92)
+ double currentValue;
+ atk_value_get_value_and_text(ATK_VALUE(element.get()), &currentValue, nullptr);
+
+ double increment = atk_value_get_increment(ATK_VALUE(element.get()));
+ atk_value_set_value(ATK_VALUE(element.get()), currentValue + factor * increment);
+#else
+ GValue currentValue = G_VALUE_INIT;
+ atk_value_get_current_value(ATK_VALUE(element.get()), &currentValue);
+
+ GValue increment = G_VALUE_INIT;
+ atk_value_get_minimum_increment(ATK_VALUE(element.get()), &increment);
+
+ GValue newValue = G_VALUE_INIT;
+ g_value_init(&newValue, G_TYPE_FLOAT);
+
+ g_value_set_float(&newValue, g_value_get_float(&currentValue) + factor * g_value_get_float(&increment));
+ atk_value_set_current_value(ATK_VALUE(element.get()), &newValue);
+
+ g_value_unset(&newValue);
+ g_value_unset(&increment);
+ g_value_unset(&currentValue);
+#endif
+}
+
+gchar* replaceCharactersForResults(gchar* str)
+{
+ WTF::String uString = WTF::String::fromUTF8(str);
+
+ // The object replacement character is passed along to ATs so we need to be
+ // able to test for their presence and do so without causing test failures.
+ uString.replace(objectReplacementCharacter, "<obj>");
+
+ // The presence of newline characters in accessible text of a single object
+ // is appropriate, but it makes test results (especially the accessible tree)
+ // harder to read.
+ uString.replace("\n", "<\\n>");
+
+ return g_strdup(uString.utf8().data());
+}
+
+const gchar* roleToString(AtkObject* object)
+{
+ AtkRole role = atk_object_get_role(object);
+
+#if ATK_CHECK_VERSION(2, 11, 3)
+ if (role == ATK_ROLE_LANDMARK) {
+ String xmlRolesValue = getAttributeSetValueForId(object, ObjectAttributeType, "xml-roles");
+ if (equalIgnoringCase(xmlRolesValue, "banner"))
+ return landmarkStringBanner;
+ if (equalIgnoringCase(xmlRolesValue, "complementary"))
+ return landmarkStringComplementary;
+ if (equalIgnoringCase(xmlRolesValue, "contentinfo"))
+ return landmarkStringContentinfo;
+ if (equalIgnoringCase(xmlRolesValue, "main"))
+ return landmarkStringMain;
+ if (equalIgnoringCase(xmlRolesValue, "navigation"))
+ return landmarkStringNavigation;
+ if (equalIgnoringCase(xmlRolesValue, "search"))
+ return landmarkStringSearch;
+ }
+#endif
+
+ switch (role) {
+ case ATK_ROLE_ALERT:
+ return "AXAlert";
+ case ATK_ROLE_DIALOG:
+ return "AXDialog";
+ case ATK_ROLE_CANVAS:
+ return "AXCanvas";
+ case ATK_ROLE_CAPTION:
+ return "AXCaption";
+ case ATK_ROLE_CHECK_BOX:
+ return "AXCheckBox";
+ case ATK_ROLE_COLOR_CHOOSER:
+ return "AXColorWell";
+ case ATK_ROLE_COLUMN_HEADER:
+ return "AXColumnHeader";
+ case ATK_ROLE_COMBO_BOX:
+ return "AXComboBox";
+ case ATK_ROLE_COMMENT:
+ return "AXComment";
+ case ATK_ROLE_DOCUMENT_FRAME:
+ return "AXDocument";
+ case ATK_ROLE_DOCUMENT_WEB:
+ return "AXWebArea";
+ case ATK_ROLE_EMBEDDED:
+ return "AXEmbedded";
+ case ATK_ROLE_ENTRY:
+ return "AXTextField";
+ case ATK_ROLE_FOOTER:
+ return "AXFooter";
+ case ATK_ROLE_FORM:
+ return "AXForm";
+ case ATK_ROLE_GROUPING:
+ return "AXGroup";
+ case ATK_ROLE_HEADING:
+ return "AXHeading";
+ case ATK_ROLE_IMAGE:
+ return "AXImage";
+ case ATK_ROLE_IMAGE_MAP:
+ return "AXImageMap";
+ case ATK_ROLE_INVALID:
+ return "AXInvalid";
+ case ATK_ROLE_LABEL:
+ return "AXLabel";
+ case ATK_ROLE_LINK:
+ return "AXLink";
+ case ATK_ROLE_LIST:
+ return "AXList";
+ case ATK_ROLE_LIST_BOX:
+ return "AXListBox";
+ case ATK_ROLE_LIST_ITEM:
+ return "AXListItem";
+ case ATK_ROLE_MENU:
+ return "AXMenu";
+ case ATK_ROLE_MENU_BAR:
+ return "AXMenuBar";
+ case ATK_ROLE_MENU_ITEM:
+ return "AXMenuItem";
+ case ATK_ROLE_PAGE_TAB:
+ return "AXTab";
+ case ATK_ROLE_PAGE_TAB_LIST:
+ return "AXTabGroup";
+ case ATK_ROLE_PANEL:
+ return "AXGroup";
+ case ATK_ROLE_PARAGRAPH:
+ return "AXParagraph";
+ case ATK_ROLE_PASSWORD_TEXT:
+ return "AXPasswordField";
+ case ATK_ROLE_PROGRESS_BAR:
+ return "AXProgressIndicator";
+ case ATK_ROLE_PUSH_BUTTON:
+ return "AXButton";
+ case ATK_ROLE_RADIO_BUTTON:
+ return "AXRadioButton";
+ case ATK_ROLE_RADIO_MENU_ITEM:
+ return "AXRadioMenuItem";
+ case ATK_ROLE_ROW_HEADER:
+ return "AXRowHeader";
+ case ATK_ROLE_CHECK_MENU_ITEM:
+ return "AXCheckMenuItem";
+ case ATK_ROLE_RULER:
+ return "AXRuler";
+ case ATK_ROLE_SCROLL_BAR:
+ return "AXScrollBar";
+ case ATK_ROLE_SCROLL_PANE:
+ return "AXScrollArea";
+ case ATK_ROLE_SECTION:
+ return "AXSection";
+ case ATK_ROLE_SEPARATOR:
+ return "AXSeparator";
+ case ATK_ROLE_SLIDER:
+ return "AXSlider";
+ case ATK_ROLE_SPIN_BUTTON:
+ return "AXSpinButton";
+ case ATK_ROLE_STATUSBAR:
+ return "AXStatusBar";
+ case ATK_ROLE_TABLE:
+ return "AXTable";
+ case ATK_ROLE_TABLE_CELL:
+ return "AXCell";
+ case ATK_ROLE_TABLE_COLUMN_HEADER:
+ return "AXColumnHeader";
+ case ATK_ROLE_TABLE_ROW:
+ return "AXRow";
+ case ATK_ROLE_TABLE_ROW_HEADER:
+ return "AXRowHeader";
+ case ATK_ROLE_TOGGLE_BUTTON:
+ return "AXToggleButton";
+ case ATK_ROLE_TOOL_BAR:
+ return "AXToolbar";
+ case ATK_ROLE_TOOL_TIP:
+ return "AXUserInterfaceTooltip";
+ case ATK_ROLE_TREE:
+ return "AXTree";
+ case ATK_ROLE_TREE_TABLE:
+ return "AXTreeGrid";
+ case ATK_ROLE_TREE_ITEM:
+ return "AXTreeItem";
+ case ATK_ROLE_WINDOW:
+ return "AXWindow";
+ case ATK_ROLE_UNKNOWN:
+ return "AXUnknown";
+#if ATK_CHECK_VERSION(2, 11, 3)
+ case ATK_ROLE_ARTICLE:
+ return "AXArticle";
+ case ATK_ROLE_AUDIO:
+ return "AXAudio";
+ case ATK_ROLE_BLOCK_QUOTE:
+ return "AXBlockquote";
+ case ATK_ROLE_DEFINITION:
+ return "AXDefinition";
+ case ATK_ROLE_LOG:
+ return "AXLog";
+ case ATK_ROLE_MARQUEE:
+ return "AXMarquee";
+ case ATK_ROLE_MATH:
+ return "AXMath";
+ case ATK_ROLE_TIMER:
+ return "AXTimer";
+ case ATK_ROLE_VIDEO:
+ return "AXVideo";
+#endif
+#if ATK_CHECK_VERSION(2, 11, 4)
+ case ATK_ROLE_DESCRIPTION_LIST:
+ return "AXDescriptionList";
+ case ATK_ROLE_DESCRIPTION_TERM:
+ return "AXDescriptionTerm";
+ case ATK_ROLE_DESCRIPTION_VALUE:
+ return "AXDescriptionValue";
+#endif
+#if ATK_CHECK_VERSION(2, 15, 2)
+ case ATK_ROLE_STATIC:
+ return "AXStatic";
+#endif
+#if ATK_CHECK_VERSION(2, 15, 4)
+ case ATK_ROLE_MATH_FRACTION:
+ return "AXMathFraction";
+ case ATK_ROLE_MATH_ROOT:
+ return "AXMathRoot";
+ case ATK_ROLE_SUBSCRIPT:
+ return "AXSubscript";
+ case ATK_ROLE_SUPERSCRIPT:
+ return "AXSuperscript";
+#endif
+ default:
+ // We want to distinguish ATK_ROLE_UNKNOWN from a known AtkRole which
+ // our DRT isn't properly handling.
+ return "FIXME not identified";
+ }
+}
+
+String attributesOfElement(AccessibilityUIElement* element)
+{
+ StringBuilder builder;
+
+ builder.append(String::format("%s\n", element->role()->string().utf8().data()));
+
+ // For the parent we print its role and its name, if available.
+ builder.append("AXParent: ");
+ RefPtr<AccessibilityUIElement> parent = element->parentElement();
+ AtkObject* atkParent = parent ? parent->platformUIElement().get() : nullptr;
+ if (atkParent) {
+ builder.append(roleToString(atkParent));
+ const char* parentName = atk_object_get_name(atkParent);
+ if (parentName && g_utf8_strlen(parentName, -1))
+ builder.append(String::format(": %s", parentName));
+ } else
+ builder.append("(null)");
+ builder.append("\n");
+
+ builder.append(String::format("AXChildren: %d\n", element->childrenCount()));
+ builder.append(String::format("AXPosition: { %f, %f }\n", element->x(), element->y()));
+ builder.append(String::format("AXSize: { %f, %f }\n", element->width(), element->height()));
+
+ String title = element->title()->string();
+ if (!title.isEmpty())
+ builder.append(String::format("%s\n", title.utf8().data()));
+
+ String description = element->description()->string();
+ if (!description.isEmpty())
+ builder.append(String::format("%s\n", description.utf8().data()));
+
+ String value = element->stringValue()->string();
+ if (!value.isEmpty())
+ builder.append(String::format("%s\n", value.utf8().data()));
+
+ builder.append(String::format("AXFocusable: %d\n", element->isFocusable()));
+ builder.append(String::format("AXFocused: %d\n", element->isFocused()));
+ builder.append(String::format("AXSelectable: %d\n", element->isSelectable()));
+ builder.append(String::format("AXSelected: %d\n", element->isSelected()));
+ builder.append(String::format("AXMultiSelectable: %d\n", element->isMultiSelectable()));
+ builder.append(String::format("AXEnabled: %d\n", element->isEnabled()));
+ builder.append(String::format("AXExpanded: %d\n", element->isExpanded()));
+ builder.append(String::format("AXRequired: %d\n", element->isRequired()));
+ builder.append(String::format("AXChecked: %d\n", element->isChecked()));
+
+ String url = element->url()->string();
+ if (!url.isEmpty())
+ builder.append(String::format("%s\n", url.utf8().data()));
+
+ // We append the ATK specific attributes as a single line at the end.
+ builder.append("AXPlatformAttributes: ");
+ builder.append(getAtkAttributeSetAsString(element->platformUIElement().get(), ObjectAttributeType));
+
+ return builder.toString();
+}
+
+static JSRetainPtr<JSStringRef> createStringWithAttributes(const Vector<RefPtr<AccessibilityUIElement> >& elements)
+{
+ StringBuilder builder;
+
+ for (Vector<RefPtr<AccessibilityUIElement> >::const_iterator it = elements.begin(); it != elements.end(); ++it) {
+ builder.append(attributesOfElement(const_cast<AccessibilityUIElement*>(it->get())));
+ builder.append("\n------------\n");
+ }
+
+ return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
+}
+
+static Vector<RefPtr<AccessibilityUIElement> > getRowHeaders(AtkTable* accessible)
+{
+ Vector<RefPtr<AccessibilityUIElement> > rowHeaders;
+
+ int rowsCount = atk_table_get_n_rows(accessible);
+ for (int row = 0; row < rowsCount; ++row)
+ rowHeaders.append(AccessibilityUIElement::create(atk_table_get_row_header(accessible, row)));
+
+ return rowHeaders;
+}
+
+static Vector<RefPtr<AccessibilityUIElement> > getColumnHeaders(AtkTable* accessible)
+{
+ Vector<RefPtr<AccessibilityUIElement> > columnHeaders;
+
+ int columnsCount = atk_table_get_n_columns(accessible);
+ for (int column = 0; column < columnsCount; ++column)
+ columnHeaders.append(AccessibilityUIElement::create(atk_table_get_column_header(accessible, column)));
+
+ return columnHeaders;
+}
+
+static Vector<RefPtr<AccessibilityUIElement> > getVisibleCells(AccessibilityUIElement* element)
+{
+ Vector<RefPtr<AccessibilityUIElement> > visibleCells;
+
+ AtkTable* accessible = ATK_TABLE(element->platformUIElement().get());
+ int rowsCount = atk_table_get_n_rows(accessible);
+ int columnsCount = atk_table_get_n_columns(accessible);
+
+ for (int row = 0; row < rowsCount; ++row) {
+ for (int column = 0; column < columnsCount; ++column)
+ visibleCells.append(element->cellForColumnAndRow(column, row));
+ }
+
+ return visibleCells;
+}
+
+#if ATK_CHECK_VERSION(2,11,90)
+static Vector<RefPtr<AccessibilityUIElement>> convertGPtrArrayToVector(const GPtrArray* array)
+{
+ Vector<RefPtr<AccessibilityUIElement>> cells;
+ for (guint i = 0; i < array->len; i++) {
+ if (AtkObject* atkObject = static_cast<AtkObject*>(g_ptr_array_index(array, i)))
+ cells.append(AccessibilityUIElement::create(atkObject));
+ }
+ return cells;
+}
+
+static JSValueRef convertToJSObjectArray(const Vector<RefPtr<AccessibilityUIElement>>& children)
+{
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+
+ size_t elementCount = children.size();
+ auto valueElements = std::make_unique<JSValueRef[]>(elementCount);
+ for (size_t i = 0; i < elementCount; i++)
+ valueElements[i] = JSObjectMake(context, children[i]->wrapperClass(), children[i].get());
+
+ return JSObjectMakeArray(context, elementCount, valueElements.get(), nullptr);
+}
+#endif
+
+#if ATK_CHECK_VERSION(2,11,92)
+static double rangeMinMaxValue(AtkValue* atkValue, RangeLimit rangeLimit)
+{
+ AtkRange* range = atk_value_get_range(atkValue);
+ if (!range)
+ return 0;
+
+ double rangeValue = 0;
+ switch (rangeLimit) {
+ case RangeLimitMinimum:
+ rangeValue = atk_range_get_lower_limit(range);
+ break;
+ case RangeLimitMaximum:
+ rangeValue = atk_range_get_upper_limit(range);
+ break;
+ };
+
+ atk_range_free(range);
+ return rangeValue;
+}
+#endif
+
+} // namespace
+
+AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
+ : m_element(element)
+{
+}
+
+AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
+ : JSWrappable()
+ , m_element(other.m_element)
+{
+}
+
+AccessibilityUIElement::~AccessibilityUIElement()
+{
+}
+
+bool AccessibilityUIElement::isEqual(AccessibilityUIElement* otherElement)
+{
+ return m_element == otherElement->platformUIElement();
+}
+
+void AccessibilityUIElement::getChildren(Vector<RefPtr<AccessibilityUIElement> >& children)
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return;
+
+ int count = childrenCount();
+ for (int i = 0; i < count; i++) {
+ GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), i));
+ children.append(AccessibilityUIElement::create(child.get()));
+ }
+}
+
+void AccessibilityUIElement::getChildrenWithRange(Vector<RefPtr<AccessibilityUIElement> >& children, unsigned location, unsigned length)
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return;
+ unsigned end = location + length;
+ for (unsigned i = location; i < end; i++) {
+ GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), i));
+ children.append(AccessibilityUIElement::create(child.get()));
+ }
+}
+
+int AccessibilityUIElement::childrenCount()
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return 0;
+
+ return atk_object_get_n_accessible_children(ATK_OBJECT(m_element.get()));
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::elementAtPoint(int x, int y)
+{
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return nullptr;
+
+ GRefPtr<AtkObject> objectAtPoint = adoptGRef(atk_component_ref_accessible_at_point(ATK_COMPONENT(m_element.get()), x, y, ATK_XY_WINDOW));
+ return AccessibilityUIElement::create(objectAtPoint ? objectAtPoint.get() : m_element.get());
+}
+
+unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return 0;
+
+ Vector<RefPtr<AccessibilityUIElement> > children;
+ getChildren(children);
+
+ unsigned count = children.size();
+ for (unsigned i = 0; i < count; i++)
+ if (children[i]->isEqual(element))
+ return i;
+
+ return 0;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::childAtIndex(unsigned index)
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return nullptr;
+
+ Vector<RefPtr<AccessibilityUIElement> > children;
+ getChildrenWithRange(children, index, 1);
+
+ if (children.size() == 1)
+ return children[0];
+
+ return nullptr;
+}
+
+static PassRefPtr<AccessibilityUIElement> accessibilityElementAtIndex(AtkObject* element, AtkRelationType relationType, unsigned index)
+{
+ if (!ATK_IS_OBJECT(element))
+ return nullptr;
+
+ AtkRelationSet* relationSet = atk_object_ref_relation_set(element);
+ if (!relationSet)
+ return nullptr;
+
+ AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, relationType);
+ if (!relation)
+ return nullptr;
+
+ GPtrArray* targetList = atk_relation_get_target(relation);
+ if (!targetList || !targetList->len || index >= targetList->len)
+ return nullptr;
+
+ AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, index));
+ g_object_unref(relationSet);
+
+ return target ? AccessibilityUIElement::create(target) : nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
+{
+ return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_FLOWS_TO, index);
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaControlsElementAtIndex(unsigned index)
+{
+ return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_CONTROLLER_FOR, index);
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::rowAtIndex(unsigned index)
+{
+ // ATK doesn't have API to get an accessible row by index directly. It does, however, have
+ // API to get cells in the row specified by index. The parent of a cell should be the row.
+ AtkTable* axTable = ATK_TABLE(m_element.get());
+ unsigned nColumns = columnCount();
+ for (unsigned col = 0; col < nColumns; col++) {
+ // Find the first cell in this row that only spans one row.
+ if (atk_table_get_row_extent_at(axTable, index, col) == 1) {
+ AtkObject* cell = atk_table_ref_at(axTable, index, col);
+ return cell ? AccessibilityUIElement::create(atk_object_get_parent(cell)) : nullptr;
+ }
+ }
+
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedChildAtIndex(unsigned index) const
+{
+ if (!ATK_SELECTION(m_element.get()))
+ return nullptr;
+
+ GRefPtr<AtkObject> child = adoptGRef(atk_selection_ref_selection(ATK_SELECTION(m_element.get()), index));
+ return child ? AccessibilityUIElement::create(child.get()) : nullptr;
+}
+
+unsigned AccessibilityUIElement::selectedChildrenCount() const
+{
+ if (!ATK_IS_SELECTION(m_element.get()))
+ return 0;
+ return atk_selection_get_selection_count(ATK_SELECTION(m_element.get()));
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedRowAtIndex(unsigned index)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement()
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return nullptr;
+
+ AtkRelationSet* set = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
+ if (!set)
+ return nullptr;
+
+ AtkObject* target = nullptr;
+ int count = atk_relation_set_get_n_relations(set);
+ for (int i = 0; i < count; i++) {
+ AtkRelation* relation = atk_relation_set_get_relation(set, i);
+ if (atk_relation_get_relation_type(relation) == ATK_RELATION_LABELLED_BY) {
+ GPtrArray* targetList = atk_relation_get_target(relation);
+ if (targetList->len)
+ target = static_cast<AtkObject*>(g_ptr_array_index(targetList, 0));
+ }
+ }
+
+ g_object_unref(set);
+ return target ? AccessibilityUIElement::create(target) : nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::parentElement()
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return nullptr;
+
+ AtkObject* parent = atk_object_get_parent(ATK_OBJECT(m_element.get()));
+ return parent ? AccessibilityUIElement::create(parent) : nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedByRow()
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfLinkedUIElements()
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfDocumentLinks()
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfChildren()
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ Vector<RefPtr<AccessibilityUIElement> > children;
+ getChildren(children);
+
+ return createStringWithAttributes(children);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::allAttributes()
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ return JSStringCreateWithUTF8CString(attributesOfElement(this).utf8().data());
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ String atkAttributeName = coreAttributeToAtkAttribute(attribute);
+
+ // Try object attributes first.
+ String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
+
+ // Try text attributes if the requested one was not found and we have an AtkText object.
+ if (attributeValue.isEmpty() && ATK_IS_TEXT(m_element.get()))
+ attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
+
+ // Additional check to make sure that the exposure of the state ATK_STATE_INVALID_ENTRY
+ // is consistent with the exposure of aria-invalid as a text attribute, if present.
+ if (atkAttributeName == attributesMap[InvalidNameIndex][AtkDomain]) {
+ bool isInvalidState = checkElementState(m_element.get(), ATK_STATE_INVALID_ENTRY);
+ if (attributeValue.isEmpty())
+ return JSStringCreateWithUTF8CString(isInvalidState ? "true" : "false");
+
+ // If the text attribute was there, check that it's consistent with
+ // what the state says or force the test to fail otherwise.
+ bool isAriaInvalid = attributeValue != "false";
+ if (isInvalidState != isAriaInvalid)
+ return JSStringCreateWithCharacters(0, 0);
+ }
+
+ return JSStringCreateWithUTF8CString(attributeValue.utf8().data());
+}
+
+double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute)
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return 0;
+
+ String atkAttributeName = coreAttributeToAtkAttribute(attribute);
+ if (atkAttributeName.isEmpty())
+ return 0;
+
+ if (atkAttributeName == "setsize" || atkAttributeName == "posinset") {
+ String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
+ if (!attributeValue.isEmpty())
+ return attributeValue.toDouble();
+ }
+
+ return 0;
+}
+
+JSValueRef AccessibilityUIElement::uiElementArrayAttributeValue(JSStringRef attribute) const
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+JSValueRef AccessibilityUIElement::rowHeaders() const
+{
+#if ATK_CHECK_VERSION(2,11,90)
+ if (!ATK_IS_TABLE_CELL(m_element.get()))
+ return nullptr;
+
+ GRefPtr<GPtrArray> array = adoptGRef(atk_table_cell_get_row_header_cells(ATK_TABLE_CELL(m_element.get())));
+ if (!array)
+ return nullptr;
+
+ Vector<RefPtr<AccessibilityUIElement>> rows = convertGPtrArrayToVector(array.get());
+ return convertToJSObjectArray(rows);
+#else
+ return nullptr;
+#endif
+}
+
+JSValueRef AccessibilityUIElement::columnHeaders() const
+{
+#if ATK_CHECK_VERSION(2,11,90)
+ if (!ATK_IS_TABLE_CELL(m_element.get()) && !ATK_IS_TABLE(m_element.get()))
+ return nullptr;
+
+ Vector<RefPtr<AccessibilityUIElement>> columns;
+ if (ATK_IS_TABLE_CELL(m_element.get())) {
+ GRefPtr<GPtrArray> array = adoptGRef(atk_table_cell_get_column_header_cells(ATK_TABLE_CELL(m_element.get())));
+ if (!array)
+ return nullptr;
+
+ columns = convertGPtrArrayToVector(array.get());
+ } else
+ columns = getColumnHeaders(ATK_TABLE(m_element.get()));
+ return convertToJSObjectArray(columns);
+#else
+ return nullptr;
+#endif
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementAttributeValue(JSStringRef attribute) const
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
+{
+ // FIXME: implement
+ return false;
+}
+
+bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return false;
+
+ String attributeString = jsStringToWTFString(attribute);
+ if (attributeString == "AXValue")
+ return checkElementState(m_element.get(), ATK_STATE_EDITABLE);
+
+ return false;
+}
+
+bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return false;
+
+ String atkAttributeName = coreAttributeToAtkAttribute(attribute);
+ if (atkAttributeName.isEmpty())
+ return false;
+
+ // For now, an attribute is supported whether it's exposed as a object or a text attribute.
+ String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
+ if (attributeValue.isEmpty())
+ attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
+
+ return !attributeValue.isEmpty();
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames()
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::role()
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ GUniquePtr<char> roleStringWithPrefix(g_strdup_printf("AXRole: %s", roleToString(ATK_OBJECT(m_element.get()))));
+ return JSStringCreateWithUTF8CString(roleStringWithPrefix.get());
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole()
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription()
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::computedRoleString()
+{
+ String role = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "computed-role");
+ if (!role.isEmpty())
+ return JSStringCreateWithUTF8CString(role.utf8().data());
+
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::title()
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ const gchar* name = atk_object_get_name(ATK_OBJECT(m_element.get()));
+ GUniquePtr<gchar> axTitle(g_strdup_printf("AXTitle: %s", name ? name : ""));
+
+ return JSStringCreateWithUTF8CString(axTitle.get());
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::description()
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ const gchar* description = atk_object_get_description(ATK_OBJECT(m_element.get()));
+ if (!description)
+ return JSStringCreateWithCharacters(0, 0);
+
+ GUniquePtr<gchar> axDesc(g_strdup_printf("AXDescription: %s", description));
+
+ return JSStringCreateWithUTF8CString(axDesc.get());
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ const gchar* axOrientation = nullptr;
+ if (checkElementState(m_element.get(), ATK_STATE_HORIZONTAL))
+ axOrientation = "AXOrientation: AXHorizontalOrientation";
+ else if (checkElementState(m_element.get(), ATK_STATE_VERTICAL))
+ axOrientation = "AXOrientation: AXVerticalOrientation";
+
+ if (!axOrientation)
+ return JSStringCreateWithCharacters(0, 0);
+
+ return JSStringCreateWithUTF8CString(axOrientation);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue()
+{
+ if (!ATK_IS_TEXT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, -1));
+ GUniquePtr<gchar> textWithReplacedCharacters(replaceCharactersForResults(text.get()));
+ GUniquePtr<gchar> axValue(g_strdup_printf("AXValue: %s", textWithReplacedCharacters.get()));
+
+ return JSStringCreateWithUTF8CString(axValue.get());
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::language()
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ const gchar* locale = atk_object_get_object_locale(ATK_OBJECT(m_element.get()));
+ if (!locale)
+ return JSStringCreateWithCharacters(0, 0);
+
+ GUniquePtr<char> axValue(g_strdup_printf("AXLanguage: %s", locale));
+ return JSStringCreateWithUTF8CString(axValue.get());
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ AtkRelationSet* relationSet = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
+ if (!relationSet)
+ return nullptr;
+
+ AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY);
+ if (!relation)
+ return nullptr;
+
+ GPtrArray* targetList = atk_relation_get_target(relation);
+ if (!targetList || !targetList->len)
+ return nullptr;
+
+ StringBuilder builder;
+ builder.append("AXHelp: ");
+
+ for (int targetCount = 0; targetCount < targetList->len; targetCount++) {
+ if (AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, targetCount))) {
+ GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(target), 0, -1));
+ if (targetCount)
+ builder.append(" ");
+ builder.append(text.get());
+ }
+ }
+
+ g_object_unref(relationSet);
+
+ return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
+}
+
+double AccessibilityUIElement::x()
+{
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
+
+ int x;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, nullptr, nullptr, ATK_XY_SCREEN);
+#else
+ atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_SCREEN);
+#endif
+ return x;
+}
+
+double AccessibilityUIElement::y()
+{
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
+
+ int y;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, nullptr, ATK_XY_SCREEN);
+#else
+ atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_SCREEN);
+#endif
+ return y;
+}
+
+double AccessibilityUIElement::width()
+{
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
+
+ int width;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, &width, nullptr, ATK_XY_WINDOW);
+#else
+ atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
+#endif
+ return width;
+}
+
+double AccessibilityUIElement::height()
+{
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
+
+ int height;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, nullptr, &height, ATK_XY_WINDOW);
+#else
+ atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
+#endif
+ return height;
+}
+
+double AccessibilityUIElement::clickPointX()
+{
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
+
+ int x, width;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, &width, nullptr, ATK_XY_WINDOW);
+#else
+ atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_WINDOW);
+ atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
+#endif
+
+ return x + width / 2.0;
+}
+
+double AccessibilityUIElement::clickPointY()
+{
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
+
+ int y, height;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, &height, ATK_XY_WINDOW);
+#else
+ atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_WINDOW);
+ atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
+#endif
+
+ return y + height / 2.0;
+}
+
+double AccessibilityUIElement::intValue() const
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return 0;
+
+ if (ATK_IS_VALUE(m_element.get())) {
+#if ATK_CHECK_VERSION(2,11,92)
+ double value;
+ atk_value_get_value_and_text(ATK_VALUE(m_element.get()), &value, nullptr);
+ return value;
+#else
+ GValue value = G_VALUE_INIT;
+ atk_value_get_current_value(ATK_VALUE(m_element.get()), &value);
+ if (!G_VALUE_HOLDS_FLOAT(&value))
+ return 0;
+ return g_value_get_float(&value);
+#endif
+ }
+
+ // Consider headings as an special case when returning the "int value" of
+ // an AccessibilityUIElement, so we can reuse some tests to check the level
+ // both for HTML headings and objects with the aria-level attribute.
+ if (atk_object_get_role(ATK_OBJECT(m_element.get())) == ATK_ROLE_HEADING) {
+ String headingLevel = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "level");
+ bool ok;
+ double headingLevelValue = headingLevel.toDouble(&ok);
+ if (ok)
+ return headingLevelValue;
+ }
+
+ return 0;
+}
+
+double AccessibilityUIElement::minValue()
+{
+ if (!ATK_IS_VALUE(m_element.get()))
+ return 0;
+#if ATK_CHECK_VERSION(2,11,92)
+ return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMinimum);
+#else
+ GValue value = G_VALUE_INIT;
+ atk_value_get_minimum_value(ATK_VALUE(m_element.get()), &value);
+ if (!G_VALUE_HOLDS_FLOAT(&value))
+ return 0;
+
+ return g_value_get_float(&value);
+#endif
+}
+
+double AccessibilityUIElement::maxValue()
+{
+ if (!ATK_IS_VALUE(m_element.get()))
+ return 0;
+
+#if ATK_CHECK_VERSION(2,11,92)
+ return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMaximum);
+#else
+ GValue value = G_VALUE_INIT;
+ atk_value_get_maximum_value(ATK_VALUE(m_element.get()), &value);
+ if (!G_VALUE_HOLDS_FLOAT(&value))
+ return 0;
+
+ return g_value_get_float(&value);
+#endif
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription()
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+int AccessibilityUIElement::insertionPointLineNumber()
+{
+ // FIXME: implement
+ return -1;
+}
+
+bool AccessibilityUIElement::isPressActionSupported()
+{
+ if (!ATK_IS_ACTION(m_element.get()))
+ return false;
+
+ const gchar* actionName = atk_action_get_name(ATK_ACTION(m_element.get()), 0);
+ return equalIgnoringCase(actionName, String("press")) || equalIgnoringCase(actionName, String("jump"));
+}
+
+bool AccessibilityUIElement::isIncrementActionSupported()
+{
+ // FIXME: implement
+ return false;
+}
+
+bool AccessibilityUIElement::isDecrementActionSupported()
+{
+ // FIXME: implement
+ return false;
+}
+
+bool AccessibilityUIElement::isEnabled()
+{
+ return checkElementState(m_element.get(), ATK_STATE_ENABLED);
+}
+
+bool AccessibilityUIElement::isRequired() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_REQUIRED);
+}
+
+bool AccessibilityUIElement::isFocused() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_FOCUSED);
+}
+
+bool AccessibilityUIElement::isSelected() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_SELECTED);
+}
+
+bool AccessibilityUIElement::isSelectedOptionActive() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_ACTIVE);
+}
+
+bool AccessibilityUIElement::isExpanded() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
+}
+
+bool AccessibilityUIElement::isChecked() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_CHECKED);
+}
+
+bool AccessibilityUIElement::isIndeterminate() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_INDETERMINATE);
+}
+
+int AccessibilityUIElement::hierarchicalLevel() const
+{
+ // FIXME: implement
+ return 0;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::speak()
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+bool AccessibilityUIElement::ariaIsGrabbed() const
+{
+ // FIXME: implement
+ return false;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+// parameterized attributes
+int AccessibilityUIElement::lineForIndex(int index)
+{
+ if (!ATK_IS_TEXT(m_element.get()))
+ return -1;
+
+ if (index < 0 || index > atk_text_get_character_count(ATK_TEXT(m_element.get())))
+ return -1;
+
+ GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, index));
+ int lineNo = 0;
+ for (gchar* offset = text.get(); *offset; ++offset) {
+ if (*offset == '\n')
+ ++lineNo;
+ }
+
+ return lineNo;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line)
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
+{
+ if (!ATK_IS_TEXT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ String string = atk_text_get_text(ATK_TEXT(m_element.get()), location, location + length);
+ return JSStringCreateWithUTF8CString(string.utf8().data());
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
+{
+ // FIXME: implement
+ return false;
+}
+
+unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
+{
+ // FIXME: implement
+ return 0;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::selectTextWithCriteria(JSContextRef context, JSStringRef ambiguityResolution, JSValueRef searchStrings, JSStringRef replacementString, JSStringRef activity)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders()
+{
+ if (!ATK_IS_TABLE(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ Vector<RefPtr<AccessibilityUIElement> > columnHeaders = getColumnHeaders(ATK_TABLE(m_element.get()));
+ return createStringWithAttributes(columnHeaders);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders()
+{
+ if (!ATK_IS_TABLE(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ Vector<RefPtr<AccessibilityUIElement> > rowHeaders = getRowHeaders(ATK_TABLE(m_element.get()));
+ return createStringWithAttributes(rowHeaders);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns()
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows()
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells()
+{
+ if (!ATK_IS_TABLE(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ Vector<RefPtr<AccessibilityUIElement> > visibleCells = getVisibleCells(this);
+ return createStringWithAttributes(visibleCells);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader()
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+int AccessibilityUIElement::rowCount()
+{
+ if (!ATK_IS_TABLE(m_element.get()))
+ return 0;
+
+ return atk_table_get_n_rows(ATK_TABLE(m_element.get()));
+}
+
+int AccessibilityUIElement::columnCount()
+{
+ if (!ATK_IS_TABLE(m_element.get()))
+ return 0;
+
+ return atk_table_get_n_columns(ATK_TABLE(m_element.get()));
+}
+
+int AccessibilityUIElement::indexInTable()
+{
+ // FIXME: implement
+ return -1;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::rowIndexRange()
+{
+ // Range in table for rows.
+ return indexRangeInTable(m_element.get(), true);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange()
+{
+ // Range in table for columns.
+ return indexRangeInTable(m_element.get(), false);
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
+{
+ if (!ATK_IS_TABLE(m_element.get()))
+ return nullptr;
+
+ // Adopt the AtkObject representing the cell because
+ // at_table_ref_at() transfers full ownership.
+ GRefPtr<AtkObject> foundCell = adoptGRef(atk_table_ref_at(ATK_TABLE(m_element.get()), row, col));
+ return foundCell ? AccessibilityUIElement::create(foundCell.get()) : nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange()
+{
+ if (!ATK_IS_TEXT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ gint start, end;
+ g_free(atk_text_get_selection(ATK_TEXT(m_element.get()), 0, &start, &end));
+
+ GUniquePtr<gchar> selection(g_strdup_printf("{%d, %d}", start, end - start));
+ return JSStringCreateWithUTF8CString(selection.get());
+}
+
+bool AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
+{
+ if (!ATK_IS_TEXT(m_element.get()))
+ return false;
+
+ if (!length)
+ return atk_text_set_caret_offset(ATK_TEXT(m_element.get()), location);
+
+ return atk_text_set_selection(ATK_TEXT(m_element.get()), 0, location, location + length);
+}
+
+void AccessibilityUIElement::increment()
+{
+ alterCurrentValue(m_element.get(), 1);
+}
+
+void AccessibilityUIElement::decrement()
+{
+ alterCurrentValue(m_element.get(), -1);
+}
+
+void AccessibilityUIElement::showMenu()
+{
+ // FIXME: implement
+}
+
+void AccessibilityUIElement::press()
+{
+ if (!ATK_IS_ACTION(m_element.get()))
+ return;
+
+ // Only one action per object is supported so far.
+ atk_action_do_action(ATK_ACTION(m_element.get()), 0);
+}
+
+void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) const
+{
+ // FIXME: implement
+}
+
+void AccessibilityUIElement::setSelectedChildAtIndex(unsigned index) const
+{
+ if (!ATK_IS_SELECTION(m_element.get()))
+ return;
+
+ atk_selection_add_selection(ATK_SELECTION(m_element.get()), index);
+}
+
+void AccessibilityUIElement::removeSelectionAtIndex(unsigned index) const
+{
+ if (!ATK_IS_SELECTION(m_element.get()))
+ return;
+
+ atk_selection_remove_selection(ATK_SELECTION(m_element.get()), index);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding()
+{
+ if (!ATK_IS_DOCUMENT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
+ if (role != ATK_ROLE_DOCUMENT_FRAME)
+ return JSStringCreateWithCharacters(0, 0);
+
+ return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "Encoding"));
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI()
+{
+ if (!ATK_IS_DOCUMENT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
+ if (role != ATK_ROLE_DOCUMENT_FRAME)
+ return JSStringCreateWithCharacters(0, 0);
+
+ return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "URI"));
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::url()
+{
+ if (!ATK_IS_HYPERLINK_IMPL(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ AtkHyperlink* hyperlink = atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(m_element.get()));
+ GUniquePtr<char> hyperlinkURI(atk_hyperlink_get_uri(hyperlink, 0));
+
+ // Build the result string, stripping the absolute URL paths if present.
+ char* localURI = g_strstr_len(hyperlinkURI.get(), -1, "LayoutTests");
+ String axURL = String::format("AXURL: %s", localURI ? localURI : hyperlinkURI.get());
+ return JSStringCreateWithUTF8CString(axURL.utf8().data());
+}
+
+bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback)
+{
+ if (!functionCallback)
+ return false;
+
+ // Only one notification listener per element.
+ if (m_notificationHandler)
+ return false;
+
+ m_notificationHandler = AccessibilityNotificationHandler::create();
+ m_notificationHandler->setPlatformElement(platformUIElement());
+ m_notificationHandler->setNotificationFunctionCallback(functionCallback);
+
+ return true;
+}
+
+bool AccessibilityUIElement::removeNotificationListener()
+{
+ // Programmers should not be trying to remove a listener that's already removed.
+ ASSERT(m_notificationHandler);
+ m_notificationHandler = nullptr;
+
+ return true;
+}
+
+bool AccessibilityUIElement::isFocusable() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_FOCUSABLE);
+}
+
+bool AccessibilityUIElement::isSelectable() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_SELECTABLE);
+}
+
+bool AccessibilityUIElement::isMultiSelectable() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_MULTISELECTABLE);
+}
+
+bool AccessibilityUIElement::isVisible() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_VISIBLE);
+}
+
+bool AccessibilityUIElement::isOffScreen() const
+{
+ // FIXME: implement
+ return false;
+}
+
+bool AccessibilityUIElement::isCollapsed() const
+{
+ // FIXME: implement
+ return false;
+}
+
+bool AccessibilityUIElement::isIgnored() const
+{
+ // FIXME: implement
+ return false;
+}
+
+bool AccessibilityUIElement::hasPopup() const
+{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return false;
+
+ String hasPopupValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "haspopup");
+ return equalIgnoringCase(hasPopupValue, "true");
+}
+
+void AccessibilityUIElement::takeFocus()
+{
+ // FIXME: implement
+}
+
+void AccessibilityUIElement::takeSelection()
+{
+ // FIXME: implement
+}
+
+void AccessibilityUIElement::addSelection()
+{
+ // FIXME: implement
+}
+
+void AccessibilityUIElement::removeSelection()
+{
+ // FIXME: implement
+}
+
+// Text markers
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::lineTextMarkerRangeForTextMarker(AccessibilityTextMarker* textMarker)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
+{
+ // FIXME: implement
+ return 0;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker* textMarker)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker* textMarker)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange* markerRange)
+{
+ // FIXME: implement
+ return JSStringCreateWithCharacters(0, 0);
+}
+
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef attribute, AccessibilityTextMarkerRange* range)
+{
+ // FIXME: implement
+ return false;
+}
+
+int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker* marker)
+{
+ // FIXME: implement
+ return -1;
+}
+
+bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker* textMarker)
+{
+ // FIXME: implement
+ return false;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int textIndex)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker()
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker()
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+bool AccessibilityUIElement::setSelectedVisibleTextRange(AccessibilityTextMarkerRange*)
+{
+ return false;
+}
+
+void AccessibilityUIElement::scrollToMakeVisible()
+{
+ // FIXME: implement
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const
+{
+ notImplemented();
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
+{
+ notImplemented();
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const
+{
+ notImplemented();
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const
+{
+ notImplemented();
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> stringAtOffset(PlatformUIElement element, AtkTextBoundary boundary, int offset)
+{
+ if (!ATK_IS_TEXT(element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ gint startOffset, endOffset;
+ StringBuilder builder;
+
+#if ATK_CHECK_VERSION(2, 10, 0)
+ AtkTextGranularity granularity;
+ switch (boundary) {
+ case ATK_TEXT_BOUNDARY_CHAR:
+ granularity = ATK_TEXT_GRANULARITY_CHAR;
+ break;
+ case ATK_TEXT_BOUNDARY_WORD_START:
+ granularity = ATK_TEXT_GRANULARITY_WORD;
+ break;
+ case ATK_TEXT_BOUNDARY_LINE_START:
+ granularity = ATK_TEXT_GRANULARITY_LINE;
+ break;
+ case ATK_TEXT_BOUNDARY_SENTENCE_START:
+ granularity = ATK_TEXT_GRANULARITY_SENTENCE;
+ break;
+ default:
+ return JSStringCreateWithCharacters(0, 0);
+ }
+
+ builder.append(atk_text_get_string_at_offset(ATK_TEXT(element.get()), offset, granularity, &startOffset, &endOffset));
+#else
+ builder.append(atk_text_get_text_at_offset(ATK_TEXT(element.get()), offset, boundary, &startOffset, &endOffset));
+#endif
+ builder.append(String::format(", %i, %i", startOffset, endOffset));
+ return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset)
+{
+ return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_CHAR, offset);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset)
+{
+ return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_WORD_START, offset);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset)
+{
+ return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_LINE_START, offset);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset)
+{
+ return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_SENTENCE_START, offset);
+}
+
+} // namespace WTR
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Tools/WebKitTestRunner/InjectedBundle/gtk/ActivateFontsGtk.cpp b/Tools/WebKitTestRunner/InjectedBundle/gtk/ActivateFontsGtk.cpp
new file mode 100644
index 000000000..496c1bcbf
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/gtk/ActivateFontsGtk.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2005, 2006 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ActivateFonts.h"
+
+#include "InjectedBundleUtilities.h"
+#include <fontconfig/fontconfig.h>
+#include <gtk/gtk.h>
+#include <wtf/glib/GLibUtilities.h>
+#include <wtf/glib/GUniquePtr.h>
+
+namespace WTR {
+
+void initializeGtkSettings()
+{
+ GtkSettings* settings = gtk_settings_get_default();
+ if (!settings)
+ return;
+ g_object_set(settings,
+ "gtk-xft-dpi", 98304,
+ "gtk-xft-antialias", 1,
+ "gtk-xft-hinting", 0,
+ "gtk-font-name", "Liberation Sans 12",
+ "gtk-theme-name", "Raleigh",
+ "gtk-icon-theme-name", "gnome",
+ "gtk-xft-rgba", "none", NULL);
+}
+
+CString getOutputDir()
+{
+ const char* webkitOutputDir = g_getenv("WEBKIT_OUTPUTDIR");
+ if (webkitOutputDir)
+ return webkitOutputDir;
+
+ CString topLevelPath = WTR::topLevelPath();
+ GUniquePtr<char> outputDir(g_build_filename(topLevelPath.data(), "WebKitBuild", nullptr));
+ return outputDir.get();
+}
+
+static CString getFontsPath()
+{
+ CString webkitOutputDir = getOutputDir();
+ GUniquePtr<char> fontsPath(g_build_filename(webkitOutputDir.data(), "DependenciesGTK", "Root", "webkitgtk-test-fonts", nullptr));
+ if (g_file_test(fontsPath.get(), static_cast<GFileTest>(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
+ return fontsPath.get();
+
+ // Try alternative fonts path.
+ fontsPath.reset(g_build_filename(webkitOutputDir.data(), "webkitgtk-test-fonts", NULL));
+ if (g_file_test(fontsPath.get(), static_cast<GFileTest>(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
+ return fontsPath.get();
+
+ return CString();
+}
+
+void initializeFontConfigSetting()
+{
+ if (g_getenv("WEBKIT_SKIP_WEBKITTESTRUNNER_FONTCONFIG_INITIALIZATION"))
+ return;
+
+ FcInit();
+
+ // If a test resulted a font being added or removed via the @font-face rule, then
+ // we want to reset the FontConfig configuration to prevent it from affecting other tests.
+ static int numFonts = 0;
+ FcFontSet* appFontSet = FcConfigGetFonts(0, FcSetApplication);
+ if (appFontSet && numFonts && appFontSet->nfont == numFonts)
+ return;
+
+ // Load our configuration file, which sets up proper aliases for family
+ // names like sans, serif and monospace.
+ FcConfig* config = FcConfigCreate();
+ GUniquePtr<gchar> fontConfigFilename(g_build_filename(FONTS_CONF_DIR, "fonts.conf", nullptr));
+ if (!g_file_test(fontConfigFilename.get(), G_FILE_TEST_IS_REGULAR))
+ g_error("Cannot find fonts.conf at %s\n", fontConfigFilename.get());
+ if (!FcConfigParseAndLoad(config, reinterpret_cast<FcChar8*>(fontConfigFilename.get()), true))
+ g_error("Couldn't load font configuration file from: %s", fontConfigFilename.get());
+
+ CString fontsPath = getFontsPath();
+ if (fontsPath.isNull())
+ g_error("Could not locate test fonts at %s. Is WEBKIT_TOP_LEVEL set?", fontsPath.data());
+
+ GUniquePtr<GDir> fontsDirectory(g_dir_open(fontsPath.data(), 0, nullptr));
+ while (const char* directoryEntry = g_dir_read_name(fontsDirectory.get())) {
+ if (!g_str_has_suffix(directoryEntry, ".ttf") && !g_str_has_suffix(directoryEntry, ".otf"))
+ continue;
+ GUniquePtr<gchar> fontPath(g_build_filename(fontsPath.data(), directoryEntry, nullptr));
+ if (!FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(fontPath.get())))
+ g_error("Could not load font at %s!", fontPath.get());
+ }
+
+ // Ahem is used by many layout tests.
+ GUniquePtr<gchar> ahemFontFilename(g_build_filename(FONTS_CONF_DIR, "AHEM____.TTF", nullptr));
+ if (!FcConfigAppFontAddFile(config, reinterpret_cast<FcChar8*>(ahemFontFilename.get())))
+ g_error("Could not load font at %s!", ahemFontFilename.get());
+
+ static const char* fontFilenames[] = {
+ "WebKitWeightWatcher100.ttf",
+ "WebKitWeightWatcher200.ttf",
+ "WebKitWeightWatcher300.ttf",
+ "WebKitWeightWatcher400.ttf",
+ "WebKitWeightWatcher500.ttf",
+ "WebKitWeightWatcher600.ttf",
+ "WebKitWeightWatcher700.ttf",
+ "WebKitWeightWatcher800.ttf",
+ "WebKitWeightWatcher900.ttf",
+ 0
+ };
+
+ for (size_t i = 0; fontFilenames[i]; ++i) {
+ GUniquePtr<gchar> fontFilename(g_build_filename(FONTS_CONF_DIR, "..", "..", "fonts", fontFilenames[i], nullptr));
+ if (!FcConfigAppFontAddFile(config, reinterpret_cast<FcChar8*>(fontFilename.get())))
+ g_error("Could not load font at %s!", fontFilename.get());
+ }
+
+ // A font with no valid Fontconfig encoding to test https://bugs.webkit.org/show_bug.cgi?id=47452
+ GUniquePtr<gchar> fontWithNoValidEncodingFilename(g_build_filename(FONTS_CONF_DIR, "FontWithNoValidEncoding.fon", nullptr));
+ if (!FcConfigAppFontAddFile(config, reinterpret_cast<FcChar8*>(fontWithNoValidEncodingFilename.get())))
+ g_error("Could not load font at %s!", fontWithNoValidEncodingFilename.get());
+
+ if (!FcConfigSetCurrent(config))
+ g_error("Could not set the current font configuration!");
+
+ numFonts = FcConfigGetFonts(config, FcSetApplication)->nfont;
+}
+
+void activateFonts()
+{
+ initializeGtkSettings();
+ initializeFontConfigSetting();
+}
+
+}
diff --git a/Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleGtk.cpp b/Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleGtk.cpp
new file mode 100644
index 000000000..ba08431c4
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleGtk.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InjectedBundle.h"
+
+#include <cstdio>
+#include <glib.h>
+
+namespace WTR {
+
+static void logHandler(const gchar* domain, GLogLevelFlags level, const gchar* message, gpointer data)
+{
+ if (level < G_LOG_LEVEL_DEBUG)
+ fprintf(stderr, "%s\n", message);
+}
+
+void InjectedBundle::platformInitialize(WKTypeRef)
+{
+ // Some plugins might try to use the GLib logger for printing debug messages. This
+ // will cause tests to fail because of unexpected output. We squelch all debug
+ // messages sent to the logger.
+ g_log_set_default_handler(logHandler, 0);
+
+ if (!g_getenv("WEBKIT_TOP_LEVEL"))
+ g_setenv("WEBKIT_TOP_LEVEL", TOP_LEVEL_DIR, FALSE);
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleUtilities.cpp b/Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleUtilities.cpp
new file mode 100644
index 000000000..afbd3f74a
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleUtilities.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InjectedBundleUtilities.h"
+
+#include <gtk/gtk.h>
+#include <wtf/glib/GLibUtilities.h>
+#include <wtf/glib/GUniquePtr.h>
+
+namespace WTR {
+
+CString topLevelPath()
+{
+ if (const char* topLevelDirectory = g_getenv("WEBKIT_TOP_LEVEL"))
+ return topLevelDirectory;
+
+ // If the environment variable wasn't provided then assume we were built into
+ // WebKitBuild/Debug or WebKitBuild/Release. Obviously this will fail if the build
+ // directory is non-standard, but we can't do much more about this.
+ GUniquePtr<char> parentPath(g_path_get_dirname(getCurrentExecutablePath().data()));
+ GUniquePtr<char> layoutTestsPath(g_build_filename(parentPath.get(), "..", "..", "..", nullptr));
+ GUniquePtr<char> absoluteTopLevelPath(realpath(layoutTestsPath.get(), 0));
+ return absoluteTopLevelPath.get();
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleUtilities.h b/Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleUtilities.h
new file mode 100644
index 000000000..4062c6d44
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/gtk/InjectedBundleUtilities.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef InjectedBundleUtilities_h
+#define InjectedBundleUtilities_h
+
+#include <wtf/text/CString.h>
+
+namespace WTR {
+
+CString topLevelPath();
+
+} // namespace WTR
+
+#endif // InjectedBundleUtilities_h
diff --git a/Tools/WebKitTestRunner/InjectedBundle/gtk/TestRunnerGtk.cpp b/Tools/WebKitTestRunner/InjectedBundle/gtk/TestRunnerGtk.cpp
new file mode 100644
index 000000000..6a36ee758
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/gtk/TestRunnerGtk.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestRunner.h"
+
+#include "InjectedBundle.h"
+#include "InjectedBundleUtilities.h"
+#include <glib.h>
+#include <wtf/glib/GUniquePtr.h>
+
+namespace WTR {
+
+void TestRunner::platformInitialize()
+{
+}
+
+void TestRunner::invalidateWaitToDumpWatchdogTimer()
+{
+ m_waitToDumpWatchdogTimer.cancel();
+}
+
+void TestRunner::initializeWaitToDumpWatchdogTimerIfNeeded()
+{
+ if (m_waitToDumpWatchdogTimer.isScheduled())
+ return;
+
+ m_waitToDumpWatchdogTimer.scheduleAfterDelay("[WTR] waitToDumpWatchdogTimerCallback", [this] { waitToDumpWatchdogTimerFired(); },
+ std::chrono::milliseconds(m_timeout));
+}
+
+JSRetainPtr<JSStringRef> TestRunner::pathToLocalResource(JSStringRef url)
+{
+ size_t urlSize = JSStringGetMaximumUTF8CStringSize(url);
+ GUniquePtr<gchar> urlString(static_cast<gchar*>(g_malloc(urlSize)));
+ JSStringGetUTF8CString(url, urlString.get(), urlSize);
+
+ if (!g_str_has_prefix(urlString.get(), "file:///tmp/LayoutTests/"))
+ return JSStringRetain(url);
+
+ const gchar* layoutTestsSuffix = urlString.get() + strlen("file:///tmp/");
+ GUniquePtr<gchar> testPath(g_build_filename(WTR::topLevelPath().data(), layoutTestsSuffix, nullptr));
+ GUniquePtr<gchar> testURI(g_filename_to_uri(testPath.get(), 0, 0));
+ return JSStringCreateWithUTF8CString(testURI.get());
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/Options.cpp b/Tools/WebKitTestRunner/Options.cpp
new file mode 100644
index 000000000..53f0253ac
--- /dev/null
+++ b/Tools/WebKitTestRunner/Options.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2013 University of Szeged. All rights reserved.
+ * Copyright (C) 2013 Samsung Electronics. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Options.h"
+
+#include <string.h>
+
+namespace WTR {
+
+Options::Options()
+ : useWaitToDumpWatchdogTimer(true)
+ , forceNoTimeout(false)
+ , verbose(false)
+ , gcBetweenTests(false)
+ , shouldDumpPixelsForAllTests(false)
+ , printSupportedFeatures(false)
+ , forceComplexText(false)
+ , shouldUseAcceleratedDrawing(false)
+ , shouldUseRemoteLayerTree(false)
+ , shouldShowWebView(false)
+{
+}
+
+bool handleOptionNoTimeout(Options& options, const char*, const char*)
+{
+ options.useWaitToDumpWatchdogTimer = false;
+ options.forceNoTimeout = true;
+ return true;
+}
+
+bool handleOptionVerbose(Options& options, const char*, const char*)
+{
+ options.verbose = true;
+ return true;
+}
+
+bool handleOptionGcBetweenTests(Options& options, const char*, const char*)
+{
+ options.gcBetweenTests = true;
+ return true;
+}
+
+bool handleOptionPixelTests(Options& options, const char*, const char*)
+{
+ options.shouldDumpPixelsForAllTests = true;
+ return true;
+}
+
+bool handleOptionPrintSupportedFeatures(Options& options, const char*, const char*)
+{
+ options.printSupportedFeatures = true;
+ return true;
+}
+
+bool handleOptionComplexText(Options& options, const char*, const char*)
+{
+ options.forceComplexText = true;
+ return true;
+}
+
+bool handleOptionAcceleratedDrawing(Options& options, const char*, const char*)
+{
+ options.shouldUseAcceleratedDrawing = true;
+ return true;
+}
+
+bool handleOptionRemoteLayerTree(Options& options, const char*, const char*)
+{
+ options.shouldUseRemoteLayerTree = true;
+ return true;
+}
+
+bool handleOptionShowWebView(Options& options, const char*, const char*)
+{
+ options.shouldShowWebView = true;
+ return true;
+}
+
+bool handleOptionAllowedHost(Options& options, const char*, const char* host)
+{
+ options.allowedHosts.push_back(host);
+ return true;
+}
+
+bool handleOptionUnmatched(Options& options, const char* option, const char*)
+{
+ if (option[0] && option[1] && option[0] == '-' && option[1] == '-')
+ return true;
+ options.paths.push_back(option);
+ return true;
+}
+
+OptionsHandler::OptionsHandler(Options& o)
+ : options(o)
+{
+ optionList.append(Option("--no-timeout", "Disables all timeouts.", handleOptionNoTimeout));
+ optionList.append(Option("--verbose", "Turns on messages.", handleOptionVerbose));
+ optionList.append(Option("--gc-between-tests", "Garbage collection between tests.", handleOptionGcBetweenTests));
+ optionList.append(Option("--pixel-tests", "Check pixels.", handleOptionPixelTests));
+ optionList.append(Option("-p", "Check pixels.", handleOptionPixelTests));
+ optionList.append(Option("--print-supported-features", "For DumpRenderTree compatibility.", handleOptionPrintSupportedFeatures));
+ optionList.append(Option("--complex-text", "Force complex tests.", handleOptionComplexText));
+ optionList.append(Option("--accelerated-drawing", "Use accelerated drawing.", handleOptionAcceleratedDrawing));
+ optionList.append(Option("--remote-layer-tree", "Use remote layer tree.", handleOptionRemoteLayerTree));
+ optionList.append(Option("--allowed-host", "Allows access to the specified host from tests.", handleOptionAllowedHost, true));
+ optionList.append(Option("--show-webview", "Show the WebView during test runs (for Debugging)", handleOptionShowWebView));
+
+ optionList.append(Option(0, 0, handleOptionUnmatched));
+}
+
+const char * OptionsHandler::usage = "Usage: WebKitTestRunner [options] filename [filename2..n]";
+const char * OptionsHandler::help = "Displays this help.";
+
+Option::Option(const char* name, const char* description, std::function<bool(Options&, const char*, const char*)> parameterHandler, bool hasArgument)
+ : name(name), description(description), parameterHandler(parameterHandler), hasArgument(hasArgument) { };
+
+bool Option::matches(const char* option)
+{
+ return !name || !strcmp(name, option);
+}
+
+bool OptionsHandler::parse(int argc, const char* argv[])
+{
+ bool status = true;
+ for (int argCounter = 1; argCounter < argc; ++argCounter) {
+ if (!strcmp(argv[argCounter], "--help") || !strcmp(argv[argCounter], "-h")) {
+ printHelp();
+ return false;
+ }
+ const char* currentOption = argv[argCounter];
+ for (Option& option : optionList) {
+ if (option.matches(currentOption)) {
+ if (!option.parameterHandler)
+ status = false;
+ else if (option.hasArgument) {
+ const char * currentArgument = argv[++argCounter];
+ if (currentArgument)
+ status &= option.parameterHandler(options, currentOption, currentArgument);
+ else
+ status = false;
+ } else
+ status &= option.parameterHandler(options, currentOption, 0);
+ break;
+ }
+ }
+ }
+ return status;
+}
+
+void OptionsHandler::printHelp(FILE* channel)
+{
+ fputs(usage, channel);
+ fputs("\n\n -h|--help\n\t", channel);
+ fputs(help, channel);
+ fputs("\n\n", channel);
+ for (Option& option : optionList) {
+ if (!option.name)
+ continue;
+ fputs(" ", channel);
+ fputs(option.name, channel);
+ fputs((option.hasArgument ? " <param>\n\t" : "\n\t"), channel);
+ fputs(option.description, channel);
+ fputs("\n\n", channel);
+ }
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/Options.h b/Tools/WebKitTestRunner/Options.h
new file mode 100644
index 000000000..5549fe464
--- /dev/null
+++ b/Tools/WebKitTestRunner/Options.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 University of Szeged. All rights reserved.
+ * Copyright (C) 2013 Samsung Electronics. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Options_h
+#define Options_h
+
+#include <functional>
+#include <stdio.h>
+#include <string>
+#include <vector>
+#include <wtf/Vector.h>
+
+namespace WTR {
+
+struct Options {
+ Options();
+ bool useWaitToDumpWatchdogTimer;
+ bool forceNoTimeout;
+ bool verbose;
+ bool gcBetweenTests;
+ bool shouldDumpPixelsForAllTests;
+ bool printSupportedFeatures;
+ bool forceComplexText;
+ bool shouldUseAcceleratedDrawing;
+ bool shouldUseRemoteLayerTree;
+ bool shouldShowWebView;
+ std::vector<std::string> paths;
+ std::vector<std::string> allowedHosts;
+};
+
+class Option {
+public:
+ Option(const char* name, const char* description, std::function<bool(Options&, const char*, const char*)> parameterHandler, bool hasArgument = false);
+ bool matches(const char*);
+ const char* name;
+ const char* description;
+ std::function<bool(Options&, const char*, const char*)> parameterHandler;
+ bool hasArgument;
+};
+
+class OptionsHandler {
+public:
+ explicit OptionsHandler(Options&);
+ bool parse(int argc, const char* argv[]);
+ void printHelp(FILE* channel = stderr);
+private:
+ Vector<Option> optionList;
+ Options& options;
+ static const char* usage;
+ static const char* help;
+};
+
+} // namespace WTR
+
+#endif // Options_h
diff --git a/Tools/WebKitTestRunner/PixelDumpSupport.cpp b/Tools/WebKitTestRunner/PixelDumpSupport.cpp
new file mode 100644
index 000000000..37b3047cb
--- /dev/null
+++ b/Tools/WebKitTestRunner/PixelDumpSupport.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2009 Apple, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PixelDumpSupport.h"
+
+#include "CyclicRedundancyCheck.h"
+#include <cstdio>
+
+static void appendIntToVector(unsigned number, Vector<unsigned char>& vector)
+{
+ size_t offset = vector.size();
+ vector.grow(offset + 4);
+ vector[offset] = ((number >> 24) & 0xff);
+ vector[offset + 1] = ((number >> 16) & 0xff);
+ vector[offset + 2] = ((number >> 8) & 0xff);
+ vector[offset + 3] = (number & 0xff);
+}
+
+static void convertChecksumToPNGComment(const char* checksum, Vector<unsigned char>& bytesToAdd)
+{
+ // Chunks of PNG files are <length>, <type>, <data>, <crc>.
+ static const char textCommentPrefix[] = "\x00\x00\x00\x29tEXtchecksum\x00";
+ static const size_t prefixLength = sizeof(textCommentPrefix) - 1; // The -1 is for the null at the end of the char[].
+ static const size_t checksumLength = 32;
+
+ bytesToAdd.append(textCommentPrefix, prefixLength);
+ bytesToAdd.append(checksum, checksumLength);
+
+ Vector<unsigned char> dataToCrc;
+ dataToCrc.append(textCommentPrefix + 4, prefixLength - 4); // Don't include the chunk length in the crc.
+ dataToCrc.append(checksum, checksumLength);
+ unsigned crc32 = computeCrc(dataToCrc);
+
+ appendIntToVector(crc32, bytesToAdd);
+}
+
+static size_t offsetAfterIHDRChunk(const unsigned char* data, const size_t dataLength)
+{
+ const int pngHeaderLength = 8;
+ const int pngIHDRChunkLength = 25; // chunk length + "IHDR" + 13 bytes of data + checksum
+ return pngHeaderLength + pngIHDRChunkLength;
+}
+
+void printPNG(const unsigned char* data, const size_t dataLength, const char* checksum)
+{
+ Vector<unsigned char> bytesToAdd;
+ convertChecksumToPNGComment(checksum, bytesToAdd);
+
+ printf("Content-Type: %s\n", "image/png");
+ printf("Content-Length: %lu\n", static_cast<unsigned long>(dataLength + bytesToAdd.size()));
+
+ size_t insertOffset = offsetAfterIHDRChunk(data, dataLength);
+
+ fwrite(data, 1, insertOffset, stdout);
+ fwrite(bytesToAdd.data(), 1, bytesToAdd.size(), stdout);
+
+ const size_t bytesToWriteInOneChunk = 1 << 15;
+ data += insertOffset;
+ size_t dataRemainingToWrite = dataLength - insertOffset;
+ while (dataRemainingToWrite) {
+ size_t bytesToWriteInThisChunk = std::min(dataRemainingToWrite, bytesToWriteInOneChunk);
+ size_t bytesWritten = fwrite(data, 1, bytesToWriteInThisChunk, stdout);
+ if (bytesWritten != bytesToWriteInThisChunk)
+ break;
+ dataRemainingToWrite -= bytesWritten;
+ data += bytesWritten;
+ }
+}
diff --git a/Tools/WebKitTestRunner/PixelDumpSupport.h b/Tools/WebKitTestRunner/PixelDumpSupport.h
new file mode 100644
index 000000000..324667f10
--- /dev/null
+++ b/Tools/WebKitTestRunner/PixelDumpSupport.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PixelDumpSupport_h
+#define PixelDumpSupport_h
+
+void printPNG(const unsigned char* data, const size_t dataLength, const char* checksum);
+
+#endif // PixelDumpSupport_h
diff --git a/Tools/WebKitTestRunner/PlatformGTK.cmake b/Tools/WebKitTestRunner/PlatformGTK.cmake
new file mode 100644
index 000000000..58e8ea8e0
--- /dev/null
+++ b/Tools/WebKitTestRunner/PlatformGTK.cmake
@@ -0,0 +1,64 @@
+add_custom_target(WebKitTestRunner-forwarding-headers
+ COMMAND ${PERL_EXECUTABLE} ${WEBKIT2_DIR}/Scripts/generate-forwarding-headers.pl --include-path ${WEBKIT_TESTRUNNER_DIR} --output ${FORWARDING_HEADERS_DIR} --platform gtk --platform soup
+)
+
+set(ForwardingHeadersForWebKitTestRunner_NAME WebKitTestRunner-forwarding-headers)
+
+list(APPEND WebKitTestRunner_SOURCES
+ ${WEBKIT_TESTRUNNER_DIR}/cairo/TestInvocationCairo.cpp
+
+ ${WEBKIT_TESTRUNNER_DIR}/gtk/TestControllerGtk.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/gtk/PlatformWebViewGtk.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/gtk/EventSenderProxyGtk.cpp
+
+ ${WEBKIT_TESTRUNNER_DIR}/gtk/EventSenderProxyGtk.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/gtk/PlatformWebViewGtk.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/gtk/TestControllerGtk.cpp
+ ${WEBKIT_TESTRUNNER_DIR}/gtk/main.cpp
+)
+
+list(APPEND WebKitTestRunner_INCLUDE_DIRECTORIES
+ ${FORWARDING_HEADERS_DIR}
+ ${WTF_DIR}/wtf/glib
+)
+
+list(APPEND WebKitTestRunner_SYSTEM_INCLUDE_DIRECTORIES
+ ${ATK_INCLUDE_DIRS}
+ ${CAIRO_INCLUDE_DIRS}
+ ${GTK3_INCLUDE_DIRS}
+ ${GLIB_INCLUDE_DIRS}
+)
+
+list(APPEND WebKitTestRunner_LIBRARIES
+ ${ATK_LIBRARIES}
+ ${CAIRO_LIBRARIES}
+ ${GTK3_LIBRARIES}
+ ${GLIB_LIBRARIES}
+ WTF
+ WebCorePlatformGTK
+)
+
+set(WebKitTestRunnerInjectedBundle_LIBRARIES
+ ${ATK_LIBRARIES}
+ ${FONTCONFIG_LIBRARIES}
+ ${GLIB_LIBRARIES}
+ ${GTK3_LIBRARIES}
+ WebCoreTestSupport
+ WebKit2
+)
+
+list(APPEND WebKitTestRunnerInjectedBundle_SOURCES
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/atk/AccessibilityControllerAtk.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/atk/AccessibilityNotificationHandlerAtk.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/atk/AccessibilityUIElementAtk.cpp
+
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/gtk/ActivateFontsGtk.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/gtk/InjectedBundleGtk.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/gtk/InjectedBundleUtilities.cpp
+ ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/gtk/TestRunnerGtk.cpp
+)
+
+add_definitions(
+ -DFONTS_CONF_DIR="${TOOLS_DIR}/WebKitTestRunner/gtk/fonts"
+ -DTOP_LEVEL_DIR="${CMAKE_SOURCE_DIR}"
+)
diff --git a/Tools/WebKitTestRunner/PlatformWebView.h b/Tools/WebKitTestRunner/PlatformWebView.h
new file mode 100644
index 000000000..14b660cf0
--- /dev/null
+++ b/Tools/WebKitTestRunner/PlatformWebView.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PlatformWebView_h
+#define PlatformWebView_h
+
+#include "ViewOptions.h"
+#include <WebKit/WKRetainPtr.h>
+
+#if defined(__APPLE__) && __APPLE__
+#ifdef __OBJC__
+@class WKView;
+@class WebKitTestRunnerWindow;
+#else
+class WKView;
+class WebKitTestRunnerWindow;
+#endif
+typedef WKView* PlatformWKView;
+typedef WebKitTestRunnerWindow* PlatformWindow;
+#elif defined(BUILDING_GTK__)
+typedef struct _GtkWidget GtkWidget;
+typedef WKViewRef PlatformWKView;
+typedef GtkWidget* PlatformWindow;
+#elif PLATFORM(EFL)
+typedef Evas_Object* PlatformWKView;
+typedef Ecore_Evas* PlatformWindow;
+#endif
+
+namespace WTR {
+
+class PlatformWebView {
+public:
+ PlatformWebView(WKContextRef, WKPageGroupRef, WKPageRef relatedPage, const ViewOptions&);
+ ~PlatformWebView();
+
+ WKPageRef page();
+ PlatformWKView platformView() { return m_view; }
+ PlatformWindow platformWindow() { return m_window; }
+ void resizeTo(unsigned width, unsigned height);
+ void focus();
+
+ // Window snapshot is always enabled by default on all other platform.
+ static bool windowSnapshotEnabled() { return true; }
+
+ WKRect windowFrame();
+ void setWindowFrame(WKRect);
+
+ void didInitializeClients();
+
+ void addChromeInputField();
+ void removeChromeInputField();
+ void makeWebViewFirstResponder();
+ void setWindowIsKey(bool isKey) { m_windowIsKey = isKey; }
+ bool windowIsKey() const { return m_windowIsKey; }
+
+ bool viewSupportsOptions(const ViewOptions&) const;
+
+ WKRetainPtr<WKImageRef> windowSnapshotImage();
+ const ViewOptions& options() const { return m_options; }
+
+ void changeWindowScaleIfNeeded(float newScale);
+
+#if PLATFORM(GTK)
+ void dismissAllPopupMenus();
+#endif
+
+private:
+ void forceWindowFramesChanged();
+
+ PlatformWKView m_view;
+ PlatformWindow m_window;
+ bool m_windowIsKey;
+ const ViewOptions m_options;
+
+#if PLATFORM(EFL)
+ bool m_usingFixedLayout;
+#endif
+};
+
+} // namespace WTR
+
+#endif // PlatformWebView_h
diff --git a/Tools/WebKitTestRunner/StringFunctions.h b/Tools/WebKitTestRunner/StringFunctions.h
new file mode 100644
index 000000000..dce761b36
--- /dev/null
+++ b/Tools/WebKitTestRunner/StringFunctions.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 University of Szeged. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef StringFunctions_h
+#define StringFunctions_h
+
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <JavaScriptCore/JavaScript.h>
+#include <WebKit/WKRetainPtr.h>
+#include <WebKit/WKString.h>
+#include <WebKit/WKStringPrivate.h>
+#include <WebKit/WKURL.h>
+#include <sstream>
+#include <string>
+#include <wtf/Platform.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+namespace WTR {
+
+// Conversion functions
+
+inline WKRetainPtr<WKStringRef> toWK(JSStringRef string)
+{
+ return adoptWK(WKStringCreateWithJSString(string));
+}
+
+inline WKRetainPtr<WKStringRef> toWK(JSRetainPtr<JSStringRef> string)
+{
+ return toWK(string.get());
+}
+
+inline WKRetainPtr<WKStringRef> toWK(const WTF::String& string)
+{
+ return adoptWK(WKStringCreateWithUTF8CString(string.utf8().data()));
+}
+
+inline JSRetainPtr<JSStringRef> toJS(WKStringRef string)
+{
+ return JSRetainPtr<JSStringRef>(Adopt, WKStringCopyJSString(string));
+}
+
+inline JSRetainPtr<JSStringRef> toJS(const WKRetainPtr<WKStringRef>& string)
+{
+ return toJS(string.get());
+}
+
+inline std::string toSTD(WKStringRef string)
+{
+ size_t bufferSize = WKStringGetMaximumUTF8CStringSize(string);
+ auto buffer = std::make_unique<char[]>(bufferSize);
+ size_t stringLength = WKStringGetUTF8CString(string, buffer.get(), bufferSize);
+ return std::string(buffer.get(), stringLength - 1);
+}
+
+inline std::string toSTD(const WKRetainPtr<WKStringRef>& string)
+{
+ return toSTD(string.get());
+}
+
+inline WTF::String toWTFString(WKStringRef string)
+{
+ size_t bufferSize = WKStringGetMaximumUTF8CStringSize(string);
+ auto buffer = std::make_unique<char[]>(bufferSize);
+ size_t stringLength = WKStringGetUTF8CString(string, buffer.get(), bufferSize);
+ return WTF::String::fromUTF8WithLatin1Fallback(buffer.get(), stringLength - 1);
+}
+
+inline WTF::String toWTFString(const WKRetainPtr<WKStringRef>& string)
+{
+ return toWTFString(string.get());
+}
+
+} // namespace WTR
+
+#endif // StringFunctions_h
diff --git a/Tools/WebKitTestRunner/TestController.cpp b/Tools/WebKitTestRunner/TestController.cpp
new file mode 100644
index 000000000..8da43e650
--- /dev/null
+++ b/Tools/WebKitTestRunner/TestController.cpp
@@ -0,0 +1,1653 @@
+/*
+ * Copyright (C) 2010, 2014-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestController.h"
+
+#include "EventSenderProxy.h"
+#include "Options.h"
+#include "PlatformWebView.h"
+#include "StringFunctions.h"
+#include "TestInvocation.h"
+#include <WebKit/WKAuthenticationChallenge.h>
+#include <WebKit/WKAuthenticationDecisionListener.h>
+#include <WebKit/WKContextConfigurationRef.h>
+#include <WebKit/WKContextPrivate.h>
+#include <WebKit/WKCookieManager.h>
+#include <WebKit/WKCredential.h>
+#include <WebKit/WKIconDatabase.h>
+#include <WebKit/WKNavigationResponseRef.h>
+#include <WebKit/WKNotification.h>
+#include <WebKit/WKNotificationManager.h>
+#include <WebKit/WKNotificationPermissionRequest.h>
+#include <WebKit/WKNumber.h>
+#include <WebKit/WKPageGroup.h>
+#include <WebKit/WKPageInjectedBundleClient.h>
+#include <WebKit/WKPagePrivate.h>
+#include <WebKit/WKPluginInformation.h>
+#include <WebKit/WKPreferencesRefPrivate.h>
+#include <WebKit/WKProtectionSpace.h>
+#include <WebKit/WKRetainPtr.h>
+#include <algorithm>
+#include <cstdio>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string>
+#include <wtf/text/CString.h>
+
+#if PLATFORM(COCOA)
+#include <WebKit/WKContextPrivateMac.h>
+#include <WebKit/WKPagePrivateMac.h>
+#endif
+
+#if !PLATFORM(COCOA)
+#include <WebKit/WKTextChecker.h>
+#endif
+
+namespace WTR {
+
+const unsigned TestController::viewWidth = 800;
+const unsigned TestController::viewHeight = 600;
+
+const unsigned TestController::w3cSVGViewWidth = 480;
+const unsigned TestController::w3cSVGViewHeight = 360;
+
+#if ASAN_ENABLED
+const double TestController::shortTimeout = 10.0;
+#else
+const double TestController::shortTimeout = 5.0;
+#endif
+
+const double TestController::noTimeout = -1;
+
+static WKURLRef blankURL()
+{
+ static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
+ return staticBlankURL;
+}
+
+static WKDataRef copyWebCryptoMasterKey(WKPageRef, const void*)
+{
+ // Any 128 bit key would do, all we need for testing is to implement the callback.
+ return WKDataCreate((const uint8_t*)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16);
+}
+
+static TestController* controller;
+
+TestController& TestController::singleton()
+{
+ ASSERT(controller);
+ return *controller;
+}
+
+TestController::TestController(int argc, const char* argv[])
+ : m_verbose(false)
+ , m_printSeparators(false)
+ , m_usingServerMode(false)
+ , m_gcBetweenTests(false)
+ , m_shouldDumpPixelsForAllTests(false)
+ , m_state(Initial)
+ , m_doneResetting(false)
+ , m_useWaitToDumpWatchdogTimer(true)
+ , m_forceNoTimeout(false)
+ , m_didPrintWebProcessCrashedMessage(false)
+ , m_shouldExitWhenWebProcessCrashes(true)
+ , m_beforeUnloadReturnValue(true)
+ , m_isGeolocationPermissionSet(false)
+ , m_isGeolocationPermissionAllowed(false)
+ , m_policyDelegateEnabled(false)
+ , m_policyDelegatePermissive(false)
+ , m_handlesAuthenticationChallenges(false)
+ , m_shouldBlockAllPlugins(false)
+ , m_forceComplexText(false)
+ , m_shouldUseAcceleratedDrawing(false)
+ , m_shouldUseRemoteLayerTree(false)
+ , m_shouldLogHistoryClientCallbacks(false)
+ , m_shouldShowWebView(false)
+{
+ initialize(argc, argv);
+ controller = this;
+ run();
+ controller = 0;
+}
+
+TestController::~TestController()
+{
+ WKIconDatabaseClose(WKContextGetIconDatabase(m_context.get()));
+
+ platformDestroy();
+}
+
+static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
+{
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ return view->windowFrame();
+}
+
+static void setWindowFrame(WKPageRef page, WKRect frame, const void* clientInfo)
+{
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ view->setWindowFrame(frame);
+}
+
+static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void*)
+{
+ printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
+ return TestController::singleton().beforeUnloadReturnValue();
+}
+
+void TestController::runModal(WKPageRef page, const void* clientInfo)
+{
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ view->setWindowIsKey(false);
+ runModal(view);
+ view->setWindowIsKey(true);
+}
+
+static void closeOtherPage(WKPageRef page, const void* clientInfo)
+{
+ WKPageClose(page);
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ delete view;
+}
+
+static void focus(WKPageRef page, const void* clientInfo)
+{
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ view->focus();
+ view->setWindowIsKey(true);
+}
+
+static void unfocus(WKPageRef page, const void* clientInfo)
+{
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ view->setWindowIsKey(false);
+}
+
+static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
+{
+ TestController::singleton().handleGeolocationPermissionRequest(permissionRequest);
+}
+
+static void decidePolicyForUserMediaPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKUserMediaPermissionRequestRef permissionRequest, const void* clientInfo)
+{
+ TestController::singleton().handleUserMediaPermissionRequest(permissionRequest);
+}
+
+WKPageRef TestController::createOtherPage(WKPageRef oldPage, WKURLRequestRef, WKDictionaryRef, WKEventModifiers, WKEventMouseButton, const void* clientInfo)
+{
+ PlatformWebView* parentView = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+
+ PlatformWebView* view = new PlatformWebView(WKPageGetContext(oldPage), WKPageGetPageGroup(oldPage), oldPage, parentView->options());
+ WKPageRef newPage = view->page();
+
+ view->resizeTo(800, 600);
+
+ WKPageUIClientV5 otherPageUIClient = {
+ { 5, view },
+ 0, // createNewPage_deprecatedForUseWithV0
+ 0, // showPage
+ closeOtherPage,
+ 0, // takeFocus
+ focus,
+ unfocus,
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // setStatusText
+ 0, // mouseDidMoveOverElement_deprecatedForUseWithV0
+ 0, // missingPluginButtonClicked
+ 0, // didNotHandleKeyEvent
+ 0, // didNotHandleWheelEvent
+ 0, // toolbarsAreVisible
+ 0, // setToolbarsAreVisible
+ 0, // menuBarIsVisible
+ 0, // setMenuBarIsVisible
+ 0, // statusBarIsVisible
+ 0, // setStatusBarIsVisible
+ 0, // isResizable
+ 0, // setIsResizable
+ getWindowFrame,
+ setWindowFrame,
+ runBeforeUnloadConfirmPanel,
+ 0, // didDraw
+ 0, // pageDidScroll
+ 0, // exceededDatabaseQuota
+ 0, // runOpenPanel
+ decidePolicyForGeolocationPermissionRequest,
+ 0, // headerHeight
+ 0, // footerHeight
+ 0, // drawHeader
+ 0, // drawFooter
+ 0, // printFrame
+ runModal,
+ 0, // didCompleteRubberBandForMainFrame
+ 0, // saveDataToFileInDownloadsFolder
+ 0, // shouldInterruptJavaScript
+ createOtherPage,
+ 0, // mouseDidMoveOverElement
+ 0, // decidePolicyForNotificationPermissionRequest
+ 0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
+ 0, // showColorPicker
+ 0, // hideColorPicker
+ 0, // unavailablePluginButtonClicked
+ 0, // pinnedStateDidChange
+ 0, // didBeginTrackingPotentialLongMousePress
+ 0, // didRecognizeLongMousePress
+ 0, // didCancelTrackingPotentialLongMousePress
+ 0, // isPlayingAudioDidChange
+ decidePolicyForUserMediaPermissionRequest,
+ 0, // didClickAutofillButton
+ 0, // runJavaScriptAlert
+ 0, // runJavaScriptConfirm
+ 0, // runJavaScriptPrompt
+ 0, // mediaSessionMetadataDidChange
+ };
+ WKPageSetPageUIClient(newPage, &otherPageUIClient.base);
+
+ WKPageNavigationClientV0 pageNavigationClient = {
+ { 0, &TestController::singleton() },
+ decidePolicyForNavigationAction,
+ decidePolicyForNavigationResponse,
+ decidePolicyForPluginLoad,
+ 0, // didStartProvisionalNavigation
+ 0, // didReceiveServerRedirectForProvisionalNavigation
+ 0, // didFailProvisionalNavigation
+ 0, // didCommitNavigation
+ 0, // didFinishNavigation
+ 0, // didFailNavigation
+ 0, // didFailProvisionalLoadInSubframe
+ 0, // didFinishDocumentLoad
+ 0, // didSameDocumentNavigation
+ 0, // renderingProgressDidChange
+ canAuthenticateAgainstProtectionSpace,
+ didReceiveAuthenticationChallenge,
+ processDidCrash,
+ copyWebCryptoMasterKey,
+ };
+ WKPageSetPageNavigationClient(newPage, &pageNavigationClient.base);
+
+ view->didInitializeClients();
+
+ TestController::singleton().updateWindowScaleForTest(view, *TestController::singleton().m_currentInvocation);
+
+ WKRetain(newPage);
+ return newPage;
+}
+
+const char* TestController::libraryPathForTesting()
+{
+ // FIXME: This may not be sufficient to prevent interactions/crashes
+ // when running more than one copy of DumpRenderTree.
+ // See https://bugs.webkit.org/show_bug.cgi?id=10906
+ char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
+ if (dumpRenderTreeTemp)
+ return dumpRenderTreeTemp;
+ return platformLibraryPathForTesting();
+}
+
+
+void TestController::initialize(int argc, const char* argv[])
+{
+ platformInitialize();
+
+ Options options;
+ OptionsHandler optionsHandler(options);
+
+ if (argc < 2) {
+ optionsHandler.printHelp();
+ exit(1);
+ }
+ if (!optionsHandler.parse(argc, argv))
+ exit(1);
+
+ m_useWaitToDumpWatchdogTimer = options.useWaitToDumpWatchdogTimer;
+ m_forceNoTimeout = options.forceNoTimeout;
+ m_verbose = options.verbose;
+ m_gcBetweenTests = options.gcBetweenTests;
+ m_shouldDumpPixelsForAllTests = options.shouldDumpPixelsForAllTests;
+ m_forceComplexText = options.forceComplexText;
+ m_shouldUseAcceleratedDrawing = options.shouldUseAcceleratedDrawing;
+ m_shouldUseRemoteLayerTree = options.shouldUseRemoteLayerTree;
+ m_paths = options.paths;
+ m_allowedHosts = options.allowedHosts;
+ m_shouldShowWebView = options.shouldShowWebView;
+
+ if (options.printSupportedFeatures) {
+ // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
+ // transforms and accelerated compositing. When we support those features, we
+ // should match DRT's behavior.
+ exit(0);
+ }
+
+ m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
+ if (m_usingServerMode)
+ m_printSeparators = true;
+ else
+ m_printSeparators = m_paths.size() > 1;
+
+ initializeInjectedBundlePath();
+ initializeTestPluginDirectory();
+
+ WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
+ m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
+
+ auto configuration = adoptWK(WKContextConfigurationCreate());
+ WKContextConfigurationSetInjectedBundlePath(configuration.get(), injectedBundlePath());
+
+ if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
+ String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
+
+ const char separator = '/';
+
+ WKContextConfigurationSetApplicationCacheDirectory(configuration.get(), toWK(temporaryFolder + separator + "ApplicationCache").get());
+ WKContextConfigurationSetDiskCacheDirectory(configuration.get(), toWK(temporaryFolder + separator + "Cache").get());
+ WKContextConfigurationSetIndexedDBDatabaseDirectory(configuration.get(), toWK(temporaryFolder + separator + "Databases" + separator + "IndexedDB").get());
+ WKContextConfigurationSetLocalStorageDirectory(configuration.get(), toWK(temporaryFolder + separator + "LocalStorage").get());
+ WKContextConfigurationSetWebSQLDatabaseDirectory(configuration.get(), toWK(temporaryFolder + separator + "Databases" + separator + "WebSQL").get());
+ WKContextConfigurationSetMediaKeysStorageDirectory(configuration.get(), toWK(temporaryFolder + separator + "MediaKeys").get());
+ }
+
+ m_context = adoptWK(WKContextCreateWithConfiguration(configuration.get()));
+ m_geolocationProvider = std::make_unique<GeolocationProviderMock>(m_context.get());
+
+#if PLATFORM(EFL)
+ WKContextSetUsesNetworkProcess(m_context.get(), false);
+ WKContextSetProcessModel(m_context.get(), kWKProcessModelSharedSecondaryProcess);
+#endif
+
+ if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
+ String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
+
+ // FIXME: This should be migrated to WKContextConfigurationRef.
+ // Disable icon database to avoid fetching <http://127.0.0.1:8000/favicon.ico> and making tests flaky.
+ // Invividual tests can enable it using testRunner.setIconDatabaseEnabled, although it's not currently supported in WebKitTestRunner.
+ WKContextSetIconDatabasePath(m_context.get(), toWK(emptyString()).get());
+ }
+
+ WKContextUseTestingNetworkSession(m_context.get());
+ WKContextSetCacheModel(m_context.get(), kWKCacheModelDocumentBrowser);
+
+ platformInitializeContext();
+
+ WKContextInjectedBundleClientV1 injectedBundleClient = {
+ { 1, this },
+ didReceiveMessageFromInjectedBundle,
+ didReceiveSynchronousMessageFromInjectedBundle,
+ 0 // getInjectedBundleInitializationUserData
+ };
+ WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient.base);
+
+ WKContextClientV1 contextClient = {
+ { 1, this },
+ 0, // plugInAutoStartOriginHashesChanged
+ networkProcessDidCrash,
+ 0, // plugInInformationBecameAvailable
+ 0, // copyWebCryptoMasterKey
+ };
+ WKContextSetClient(m_context.get(), &contextClient.base);
+
+ WKContextHistoryClientV0 historyClient = {
+ { 0, this },
+ didNavigateWithNavigationData,
+ didPerformClientRedirect,
+ didPerformServerRedirect,
+ didUpdateHistoryTitle,
+ 0, // populateVisitedLinks
+ };
+ WKContextSetHistoryClient(m_context.get(), &historyClient.base);
+
+ WKNotificationManagerRef notificationManager = WKContextGetNotificationManager(m_context.get());
+ WKNotificationProviderV0 notificationKit = m_webNotificationProvider.provider();
+ WKNotificationManagerSetProvider(notificationManager, &notificationKit.base);
+
+ if (testPluginDirectory())
+ WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
+
+ if (m_forceComplexText)
+ WKContextSetAlwaysUsesComplexTextCodePath(m_context.get(), true);
+
+ // Some preferences (notably mock scroll bars setting) currently cannot be re-applied to an existing view, so we need to set them now.
+ resetPreferencesToConsistentValues();
+
+ ViewOptions viewOptions;
+ viewOptions.useRemoteLayerTree = m_shouldUseRemoteLayerTree;
+ viewOptions.shouldShowWebView = m_shouldShowWebView;
+
+ createWebViewWithOptions(viewOptions);
+}
+
+void TestController::createWebViewWithOptions(const ViewOptions& options)
+{
+ m_mainWebView = std::make_unique<PlatformWebView>(m_context.get(), m_pageGroup.get(), nullptr, options);
+ WKPageUIClientV5 pageUIClient = {
+ { 5, m_mainWebView.get() },
+ 0, // createNewPage_deprecatedForUseWithV0
+ 0, // showPage
+ 0, // close
+ 0, // takeFocus
+ focus,
+ unfocus,
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // setStatusText
+ 0, // mouseDidMoveOverElement_deprecatedForUseWithV0
+ 0, // missingPluginButtonClicked
+ 0, // didNotHandleKeyEvent
+ 0, // didNotHandleWheelEvent
+ 0, // toolbarsAreVisible
+ 0, // setToolbarsAreVisible
+ 0, // menuBarIsVisible
+ 0, // setMenuBarIsVisible
+ 0, // statusBarIsVisible
+ 0, // setStatusBarIsVisible
+ 0, // isResizable
+ 0, // setIsResizable
+ getWindowFrame,
+ setWindowFrame,
+ runBeforeUnloadConfirmPanel,
+ 0, // didDraw
+ 0, // pageDidScroll
+ 0, // exceededDatabaseQuota,
+ 0, // runOpenPanel
+ decidePolicyForGeolocationPermissionRequest,
+ 0, // headerHeight
+ 0, // footerHeight
+ 0, // drawHeader
+ 0, // drawFooter
+ 0, // printFrame
+ runModal,
+ 0, // didCompleteRubberBandForMainFrame
+ 0, // saveDataToFileInDownloadsFolder
+ 0, // shouldInterruptJavaScript
+ createOtherPage,
+ 0, // mouseDidMoveOverElement
+ decidePolicyForNotificationPermissionRequest, // decidePolicyForNotificationPermissionRequest
+ 0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
+ 0, // showColorPicker
+ 0, // hideColorPicker
+ unavailablePluginButtonClicked,
+ 0, // pinnedStateDidChange
+ 0, // didBeginTrackingPotentialLongMousePress
+ 0, // didRecognizeLongMousePress
+ 0, // didCancelTrackingPotentialLongMousePress
+ 0, // isPlayingAudioDidChange
+ decidePolicyForUserMediaPermissionRequest,
+ 0, // didClickAutofillButton
+ 0, // runJavaScriptAlert
+ 0, // runJavaScriptConfirm
+ 0, // runJavaScriptPrompt
+ 0, // mediaSessionMetadataDidChange
+ };
+ WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient.base);
+
+ WKPageNavigationClientV0 pageNavigationClient = {
+ { 0, this },
+ decidePolicyForNavigationAction,
+ decidePolicyForNavigationResponse,
+ decidePolicyForPluginLoad,
+ 0, // didStartProvisionalNavigation
+ 0, // didReceiveServerRedirectForProvisionalNavigation
+ 0, // didFailProvisionalNavigation
+ didCommitNavigation,
+ didFinishNavigation,
+ 0, // didFailNavigation
+ 0, // didFailProvisionalLoadInSubframe
+ 0, // didFinishDocumentLoad
+ 0, // didSameDocumentNavigation
+ 0, // renderingProgressDidChange
+ canAuthenticateAgainstProtectionSpace,
+ didReceiveAuthenticationChallenge,
+ processDidCrash,
+ copyWebCryptoMasterKey,
+ };
+ WKPageSetPageNavigationClient(m_mainWebView->page(), &pageNavigationClient.base);
+
+
+ // this should just be done on the page?
+ WKPageInjectedBundleClientV0 injectedBundleClient = {
+ { 0, this },
+ didReceivePageMessageFromInjectedBundle,
+ didReceiveSynchronousPageMessageFromInjectedBundle
+ };
+ WKPageSetPageInjectedBundleClient(m_mainWebView->page(), &injectedBundleClient.base);
+
+ m_mainWebView->didInitializeClients();
+
+ // Generally, the tests should default to running at 1x. updateWindowScaleForTest() will adjust the scale to
+ // something else for specific tests that need to run at a different window scale.
+ m_mainWebView->changeWindowScaleIfNeeded(1);
+}
+
+void TestController::ensureViewSupportsOptions(const ViewOptions& options)
+{
+ if (m_mainWebView && !m_mainWebView->viewSupportsOptions(options)) {
+ WKPageSetPageUIClient(m_mainWebView->page(), 0);
+ WKPageSetPageNavigationClient(m_mainWebView->page(), 0);
+ WKPageClose(m_mainWebView->page());
+
+ m_mainWebView = nullptr;
+
+ createWebViewWithOptions(options);
+ resetStateToConsistentValues();
+ }
+}
+
+void TestController::resetPreferencesToConsistentValues()
+{
+ // Reset preferences
+ WKPreferencesRef preferences = WKPageGroupGetPreferences(m_pageGroup.get());
+ WKPreferencesResetTestRunnerOverrides(preferences);
+ WKPreferencesSetPageVisibilityBasedProcessSuppressionEnabled(preferences, false);
+ WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
+ WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
+ WKPreferencesSetAntialiasedFontDilationEnabled(preferences, false);
+ WKPreferencesSetXSSAuditorEnabled(preferences, false);
+ WKPreferencesSetWebAudioEnabled(preferences, true);
+ WKPreferencesSetMediaStreamEnabled(preferences, true);
+ WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
+ WKPreferencesSetJavaScriptRuntimeFlags(preferences, kWKJavaScriptRuntimeFlagsAllEnabled);
+ WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
+ WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
+ WKPreferencesSetDOMPasteAllowed(preferences, true);
+ WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
+ WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
+#if ENABLE(FULLSCREEN_API)
+ WKPreferencesSetFullScreenEnabled(preferences, true);
+#endif
+ WKPreferencesSetPageCacheEnabled(preferences, false);
+ WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
+ WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
+ WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);
+ WKPreferencesSetTabToLinksEnabled(preferences, false);
+ WKPreferencesSetInteractiveFormValidationEnabled(preferences, true);
+ WKPreferencesSetMockScrollbarsEnabled(preferences, true);
+
+ static WKStringRef defaultTextEncoding = WKStringCreateWithUTF8CString("ISO-8859-1");
+ WKPreferencesSetDefaultTextEncodingName(preferences, defaultTextEncoding);
+
+ static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
+ static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
+ static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
+ static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
+ static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
+ static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
+ static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
+
+ WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
+ WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
+ WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
+ WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
+ WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
+ WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
+ WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
+ WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false);
+#if ENABLE(WEB_AUDIO)
+ WKPreferencesSetMediaSourceEnabled(preferences, true);
+#endif
+
+ WKPreferencesSetHiddenPageDOMTimerThrottlingEnabled(preferences, false);
+ WKPreferencesSetHiddenPageCSSAnimationSuspensionEnabled(preferences, false);
+
+ WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing);
+
+ WKCookieManagerDeleteAllCookies(WKContextGetCookieManager(m_context.get()));
+
+ platformResetPreferencesToConsistentValues();
+}
+
+bool TestController::resetStateToConsistentValues()
+{
+ m_state = Resetting;
+
+ m_beforeUnloadReturnValue = true;
+
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
+ WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
+ WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
+ WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
+
+ WKRetainPtr<WKStringRef> allowedHostsKey = adoptWK(WKStringCreateWithUTF8CString("AllowedHosts"));
+ WKRetainPtr<WKMutableArrayRef> allowedHostsValue = adoptWK(WKMutableArrayCreate());
+ for (auto& host : m_allowedHosts) {
+ WKRetainPtr<WKStringRef> wkHost = adoptWK(WKStringCreateWithUTF8CString(host.c_str()));
+ WKArrayAppendItem(allowedHostsValue.get(), wkHost.get());
+ }
+ WKDictionarySetItem(resetMessageBody.get(), allowedHostsKey.get(), allowedHostsValue.get());
+
+ WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), resetMessageBody.get());
+
+ WKContextSetShouldUseFontSmoothing(TestController::singleton().context(), false);
+
+ WKContextSetCacheModel(TestController::singleton().context(), kWKCacheModelDocumentBrowser);
+
+ // FIXME: This function should also ensure that there is only one page open.
+
+ // Reset the EventSender for each test.
+ m_eventSenderProxy = std::make_unique<EventSenderProxy>(this);
+
+ // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is
+ // some other code doing this, it should probably be responsible for cleanup too.
+ resetPreferencesToConsistentValues();
+
+#if !PLATFORM(COCOA)
+ WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
+#endif
+
+ // in the case that a test using the chrome input field failed, be sure to clean up for the next test
+ m_mainWebView->removeChromeInputField();
+ m_mainWebView->focus();
+
+ // Re-set to the default backing scale factor by setting the custom scale factor to 0.
+ WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
+
+ WKPageClearWheelEventTestTrigger(m_mainWebView->page());
+
+#if PLATFORM(EFL)
+ // EFL use a real window while other ports such as Qt don't.
+ // In EFL, we need to resize the window to the original size after calls to window.resizeTo.
+ WKRect rect = m_mainWebView->windowFrame();
+ m_mainWebView->setWindowFrame(WKRectMake(rect.origin.x, rect.origin.y, TestController::viewWidth, TestController::viewHeight));
+#endif
+
+ // Reset notification permissions
+ m_webNotificationProvider.reset();
+
+ // Reset Geolocation permissions.
+ m_geolocationPermissionRequests.clear();
+ m_isGeolocationPermissionSet = false;
+ m_isGeolocationPermissionAllowed = false;
+
+ // Reset UserMedia permissions.
+ m_userMediaPermissionRequests.clear();
+ m_isUserMediaPermissionSet = false;
+ m_isUserMediaPermissionAllowed = false;
+
+ // Reset Custom Policy Delegate.
+ setCustomPolicyDelegate(false, false);
+
+ m_workQueueManager.clearWorkQueue();
+
+ m_handlesAuthenticationChallenges = false;
+ m_authenticationUsername = String();
+ m_authenticationPassword = String();
+
+ m_shouldBlockAllPlugins = false;
+
+ m_shouldLogHistoryClientCallbacks = false;
+
+ WKPageGroupRemoveAllUserContentFilters(WKPageGetPageGroup(m_mainWebView->page()));
+
+ setHidden(false);
+
+ // Reset main page back to about:blank
+ m_doneResetting = false;
+
+ WKPageLoadURL(m_mainWebView->page(), blankURL());
+ runUntil(m_doneResetting, shortTimeout);
+ return m_doneResetting;
+}
+
+void TestController::terminateWebContentProcess()
+{
+ WKPageTerminate(m_mainWebView->page());
+}
+
+void TestController::reattachPageToWebProcess()
+{
+ // Loading a web page is the only way to reattach an existing page to a process.
+ m_doneResetting = false;
+ WKPageLoadURL(m_mainWebView->page(), blankURL());
+ runUntil(m_doneResetting, shortTimeout);
+}
+
+const char* TestController::webProcessName()
+{
+ // FIXME: Find a way to not hardcode the process name.
+#if PLATFORM(COCOA)
+ return "com.apple.WebKit.WebContent.Development";
+#else
+ return "WebProcess";
+#endif
+}
+
+const char* TestController::networkProcessName()
+{
+ // FIXME: Find a way to not hardcode the process name.
+#if PLATFORM(COCOA)
+ return "com.apple.WebKit.Networking.Development";
+#else
+ return "NetworkProcess";
+#endif
+}
+
+void TestController::updateWebViewSizeForTest(const TestInvocation& test)
+{
+ bool isSVGW3CTest = test.urlContains("svg/W3C-SVG-1.1") || test.urlContains("svg\\W3C-SVG-1.1");
+
+ unsigned width = viewWidth;
+ unsigned height = viewHeight;
+ if (isSVGW3CTest) {
+ width = w3cSVGViewWidth;
+ height = w3cSVGViewHeight;
+ }
+
+ mainWebView()->resizeTo(width, height);
+}
+
+void TestController::updateWindowScaleForTest(PlatformWebView* view, const TestInvocation& test)
+{
+ bool needsHighDPIWindow = test.urlContains("/hidpi-");
+ view->changeWindowScaleIfNeeded(needsHighDPIWindow ? 2 : 1);
+}
+
+// FIXME: move into relevant platformConfigureViewForTest()?
+static bool shouldUseFixedLayout(const TestInvocation& test)
+{
+#if ENABLE(CSS_DEVICE_ADAPTATION)
+ if (test.urlContains("device-adapt/") || test.urlContains("device-adapt\\"))
+ return true;
+#endif
+
+#if USE(COORDINATED_GRAPHICS) && PLATFORM(EFL)
+ if (test.urlContains("sticky/") || test.urlContains("sticky\\"))
+ return true;
+#endif
+ return false;
+
+ UNUSED_PARAM(test);
+}
+
+void TestController::updateLayoutTypeForTest(const TestInvocation& test)
+{
+ ViewOptions viewOptions;
+
+ viewOptions.useFixedLayout = shouldUseFixedLayout(test);
+
+ ensureViewSupportsOptions(viewOptions);
+}
+
+#if !PLATFORM(COCOA) && !PLATFORM(GTK)
+void TestController::platformConfigureViewForTest(const TestInvocation&)
+{
+}
+
+void TestController::platformResetPreferencesToConsistentValues()
+{
+}
+#endif
+
+void TestController::configureViewForTest(const TestInvocation& test)
+{
+ updateWebViewSizeForTest(test);
+ updateWindowScaleForTest(mainWebView(), test);
+ updateLayoutTypeForTest(test);
+
+ platformConfigureViewForTest(test);
+}
+
+struct TestCommand {
+ TestCommand() : shouldDumpPixels(false), timeout(0) { }
+
+ std::string pathOrURL;
+ bool shouldDumpPixels;
+ std::string expectedPixelHash;
+ int timeout;
+};
+
+class CommandTokenizer {
+public:
+ explicit CommandTokenizer(const std::string& input)
+ : m_input(input)
+ , m_posNextSeparator(0)
+ {
+ pump();
+ }
+
+ bool hasNext() const;
+ std::string next();
+
+private:
+ void pump();
+ static const char kSeparator = '\'';
+ const std::string& m_input;
+ std::string m_next;
+ size_t m_posNextSeparator;
+};
+
+void CommandTokenizer::pump()
+{
+ if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
+ m_next = std::string();
+ return;
+ }
+ size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
+ m_posNextSeparator = m_input.find(kSeparator, start);
+ size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
+ m_next = std::string(m_input, start, size);
+}
+
+std::string CommandTokenizer::next()
+{
+ ASSERT(hasNext());
+
+ std::string oldNext = m_next;
+ pump();
+ return oldNext;
+}
+
+bool CommandTokenizer::hasNext() const
+{
+ return !m_next.empty();
+}
+
+NO_RETURN static void die(const std::string& inputLine)
+{
+ fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
+ exit(1);
+}
+
+TestCommand parseInputLine(const std::string& inputLine)
+{
+ TestCommand result;
+ CommandTokenizer tokenizer(inputLine);
+ if (!tokenizer.hasNext())
+ die(inputLine);
+
+ std::string arg = tokenizer.next();
+ result.pathOrURL = arg;
+ while (tokenizer.hasNext()) {
+ arg = tokenizer.next();
+ if (arg == std::string("--timeout")) {
+ std::string timeoutToken = tokenizer.next();
+ result.timeout = atoi(timeoutToken.c_str());
+ } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
+ result.shouldDumpPixels = true;
+ if (tokenizer.hasNext())
+ result.expectedPixelHash = tokenizer.next();
+ } else
+ die(inputLine);
+ }
+ return result;
+}
+
+bool TestController::runTest(const char* inputLine)
+{
+ TestCommand command = parseInputLine(std::string(inputLine));
+
+ m_state = RunningTest;
+
+ m_currentInvocation = std::make_unique<TestInvocation>(command.pathOrURL);
+ if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests)
+ m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
+ if (command.timeout > 0)
+ m_currentInvocation->setCustomTimeout(command.timeout);
+
+ platformWillRunTest(*m_currentInvocation);
+
+ m_currentInvocation->invoke();
+ m_currentInvocation = nullptr;
+
+ return true;
+}
+
+void TestController::runTestingServerLoop()
+{
+ char filenameBuffer[2048];
+ while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
+ char* newLineCharacter = strchr(filenameBuffer, '\n');
+ if (newLineCharacter)
+ *newLineCharacter = '\0';
+
+ if (strlen(filenameBuffer) == 0)
+ continue;
+
+ if (!runTest(filenameBuffer))
+ break;
+ }
+}
+
+void TestController::run()
+{
+ if (!resetStateToConsistentValues()) {
+ TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n");
+ return;
+ }
+
+ if (m_usingServerMode)
+ runTestingServerLoop();
+ else {
+ for (size_t i = 0; i < m_paths.size(); ++i) {
+ if (!runTest(m_paths[i].c_str()))
+ break;
+ }
+ }
+}
+
+void TestController::runUntil(bool& done, double timeout)
+{
+ if (m_forceNoTimeout)
+ timeout = noTimeout;
+
+ platformRunUntil(done, timeout);
+}
+
+// WKContextInjectedBundleClient
+
+void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
+}
+
+void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
+{
+ *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
+}
+
+// WKPageInjectedBundleClient
+
+void TestController::didReceivePageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
+}
+
+void TestController::didReceiveSynchronousPageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
+{
+ *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
+}
+
+void TestController::networkProcessDidCrash(WKContextRef context, const void *clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->networkProcessDidCrash();
+}
+
+void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous)
+{
+ WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
+ WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
+
+ WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
+ WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
+
+ WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
+ unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
+
+ if (synchronous)
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+
+ m_eventSenderProxy->keyDown(key, modifiers, location);
+
+ if (synchronous)
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+}
+
+void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
+{
+ if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
+ WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
+ unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
+
+ WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
+ WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
+ m_eventSenderProxy->mouseDown(button, modifiers);
+ else
+ m_eventSenderProxy->mouseUp(button, modifiers);
+
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
+ didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false);
+
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
+
+ WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
+ int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
+ WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
+ int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
+
+ return;
+ }
+
+ ASSERT_NOT_REACHED();
+ }
+
+ if (!m_currentInvocation)
+ return;
+
+ m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
+}
+
+WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
+{
+ if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
+ didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
+
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
+ WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
+ unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
+
+ WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
+ WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
+ m_eventSenderProxy->mouseDown(button, modifiers);
+ else
+ m_eventSenderProxy->mouseUp(button, modifiers);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseMoveTo(x, y);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+#if PLATFORM(MAC)
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceDown")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseForceDown();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceUp")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseForceUp();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceChanged")) {
+ WKRetainPtr<WKStringRef> forceKey = adoptWK(WKStringCreateWithUTF8CString("Force"));
+ double force = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, forceKey.get())));
+
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseForceChanged(force);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+#endif // PLATFORM(MAC)
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseScrollBy(x, y);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
+
+ WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
+ int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
+ WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
+ int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
+
+ WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
+ bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
+ WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
+ unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
+
+ m_eventSenderProxy->leapForward(time);
+ return 0;
+ }
+
+#if ENABLE(TOUCH_EVENTS)
+ if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
+
+ m_eventSenderProxy->addTouchPoint(x, y);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
+ WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
+ int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
+
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
+
+ m_eventSenderProxy->updateTouchPoint(index, x, y);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
+ WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
+ WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
+
+ WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
+ bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
+
+ m_eventSenderProxy->setTouchModifier(modifier, enable);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
+ int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
+ int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
+
+ m_eventSenderProxy->setTouchPointRadius(x, y);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->touchStart();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->touchMove();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->touchEnd();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->touchCancel();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
+ m_eventSenderProxy->clearTouchPoints();
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
+ WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
+ int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
+ m_eventSenderProxy->releaseTouchPoint(index);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
+ WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
+ int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
+ m_eventSenderProxy->cancelTouchPoint(index);
+ return 0;
+ }
+#endif
+ ASSERT_NOT_REACHED();
+ }
+ return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
+}
+
+// WKContextClient
+
+void TestController::networkProcessDidCrash()
+{
+#if PLATFORM(COCOA)
+ pid_t pid = WKContextGetNetworkProcessIdentifier(m_context.get());
+ fprintf(stderr, "#CRASHED - %s (pid %ld)\n", networkProcessName(), static_cast<long>(pid));
+#else
+ fprintf(stderr, "#CRASHED - %s\n", networkProcessName());
+#endif
+ exit(1);
+}
+
+// WKPageNavigationClient
+
+void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation);
+}
+
+void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation);
+}
+
+bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef, WKProtectionSpaceRef protectionSpace, const void*)
+{
+ WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
+
+ if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
+ std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
+ return host == "localhost" || host == "127.0.0.1";
+ }
+
+ return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest;
+}
+
+void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge);
+}
+
+void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
+}
+
+WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
+{
+ return static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForPluginLoad(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
+}
+
+WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
+{
+ if (m_shouldBlockAllPlugins)
+ return kWKPluginLoadPolicyBlocked;
+
+#if PLATFORM(MAC)
+ WKStringRef bundleIdentifier = (WKStringRef)WKDictionaryGetItemForKey(pluginInformation, WKPluginInformationBundleIdentifierKey());
+ if (!bundleIdentifier)
+ return currentPluginLoadPolicy;
+
+ if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.QuickTime Plugin.plugin"))
+ return currentPluginLoadPolicy;
+
+ if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.testnetscapeplugin"))
+ return currentPluginLoadPolicy;
+
+ RELEASE_ASSERT_NOT_REACHED(); // Please don't use any other plug-ins in tests, as they will not be installed on all machines.
+#else
+ return currentPluginLoadPolicy;
+#endif
+}
+
+void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation)
+{
+ mainWebView()->focus();
+}
+
+void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation)
+{
+ if (m_state != Resetting)
+ return;
+
+ WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(WKPageGetMainFrame(page)));
+ if (!WKURLIsEqual(wkURL.get(), blankURL()))
+ return;
+
+ m_doneResetting = true;
+ singleton().notifyDone();
+}
+
+void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge)
+{
+ WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge);
+ WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
+
+ if (WKProtectionSpaceGetAuthenticationScheme(protectionSpace) == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
+ // Any non-empty credential signals to accept the server trust. Since the cross-platform API
+ // doesn't expose a way to create a credential from server trust, we use a password credential.
+
+ WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust").get(), toWK("").get(), kWKCredentialPersistenceNone));
+ WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
+ return;
+ }
+
+ std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
+ int port = WKProtectionSpaceGetPort(protectionSpace);
+ String message = String::format("%s:%d - didReceiveAuthenticationChallenge - ", host.c_str(), port);
+ if (!m_handlesAuthenticationChallenges)
+ message.append("Simulating cancelled authentication sheet\n");
+ else
+ message.append(String::format("Responding with %s:%s\n", m_authenticationUsername.utf8().data(), m_authenticationPassword.utf8().data()));
+ m_currentInvocation->outputText(message);
+
+ if (!m_handlesAuthenticationChallenges) {
+ WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
+ return;
+ }
+ WKRetainPtr<WKStringRef> username(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
+ WKRetainPtr<WKStringRef> password(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
+ WKRetainPtr<WKCredentialRef> credential(AdoptWK, WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
+ WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
+}
+
+void TestController::processDidCrash()
+{
+ // This function can be called multiple times when crash logs are being saved on Windows, so
+ // ensure we only print the crashed message once.
+ if (!m_didPrintWebProcessCrashedMessage) {
+#if PLATFORM(COCOA)
+ pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
+ fprintf(stderr, "#CRASHED - %s (pid %ld)\n", webProcessName(), static_cast<long>(pid));
+#else
+ fprintf(stderr, "#CRASHED - %s\n", webProcessName());
+#endif
+ fflush(stderr);
+ m_didPrintWebProcessCrashedMessage = true;
+ }
+
+ if (m_shouldExitWhenWebProcessCrashes)
+ exit(1);
+}
+
+void TestController::simulateWebNotificationClick(uint64_t notificationID)
+{
+ m_webNotificationProvider.simulateWebNotificationClick(notificationID);
+}
+
+void TestController::setGeolocationPermission(bool enabled)
+{
+ m_isGeolocationPermissionSet = true;
+ m_isGeolocationPermissionAllowed = enabled;
+ decidePolicyForGeolocationPermissionRequestIfPossible();
+}
+
+void TestController::setMockGeolocationPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed)
+{
+ m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
+}
+
+void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
+{
+ m_geolocationProvider->setPositionUnavailableError(errorMessage);
+}
+
+void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
+{
+ m_geolocationPermissionRequests.append(geolocationPermissionRequest);
+ decidePolicyForGeolocationPermissionRequestIfPossible();
+}
+
+bool TestController::isGeolocationProviderActive() const
+{
+ return m_geolocationProvider->isActive();
+}
+
+void TestController::setUserMediaPermission(bool enabled)
+{
+ m_isUserMediaPermissionSet = true;
+ m_isUserMediaPermissionAllowed = enabled;
+ decidePolicyForUserMediaPermissionRequestIfPossible();
+}
+
+void TestController::handleUserMediaPermissionRequest(WKUserMediaPermissionRequestRef userMediaPermissionRequest)
+{
+ m_userMediaPermissionRequests.append(userMediaPermissionRequest);
+ decidePolicyForUserMediaPermissionRequestIfPossible();
+}
+
+void TestController::decidePolicyForUserMediaPermissionRequestIfPossible()
+{
+ if (!m_isUserMediaPermissionSet)
+ return;
+
+ for (auto& request : m_userMediaPermissionRequests) {
+ if (m_isUserMediaPermissionAllowed)
+ WKUserMediaPermissionRequestAllow(request.get());
+ else
+ WKUserMediaPermissionRequestDeny(request.get());
+ }
+ m_userMediaPermissionRequests.clear();
+}
+
+void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
+{
+ m_policyDelegateEnabled = enabled;
+ m_policyDelegatePermissive = permissive;
+}
+
+void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
+{
+ if (!m_isGeolocationPermissionSet)
+ return;
+
+ for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
+ WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
+ if (m_isGeolocationPermissionAllowed)
+ WKGeolocationPermissionRequestAllow(permissionRequest);
+ else
+ WKGeolocationPermissionRequestDeny(permissionRequest);
+ }
+ m_geolocationPermissionRequests.clear();
+}
+
+void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
+{
+ TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request);
+}
+
+void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
+{
+ WKNotificationPermissionRequestAllow(request);
+}
+
+void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
+{
+ printf("MISSING PLUGIN BUTTON PRESSED\n");
+}
+
+void TestController::decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(listener);
+}
+
+void TestController::decidePolicyForNavigationAction(WKFramePolicyListenerRef listener)
+{
+ if (m_policyDelegateEnabled && !m_policyDelegatePermissive) {
+ WKFramePolicyListenerIgnore(listener);
+ return;
+ }
+
+ WKFramePolicyListenerUse(listener);
+}
+
+void TestController::decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationResponse(navigationResponse, listener);
+}
+
+void TestController::decidePolicyForNavigationResponse(WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener)
+{
+ // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
+ // so we have to re-check again.
+ if (WKNavigationResponseCanShowMIMEType(navigationResponse)) {
+ WKFramePolicyListenerUse(listener);
+ return;
+ }
+
+ WKFramePolicyListenerIgnore(listener);
+}
+
+void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
+}
+
+void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
+{
+ if (m_state != RunningTest)
+ return;
+
+ if (!m_shouldLogHistoryClientCallbacks)
+ return;
+
+ // URL
+ WKRetainPtr<WKURLRef> urlWK = adoptWK(WKNavigationDataCopyURL(navigationData));
+ WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(urlWK.get()));
+ // Title
+ WKRetainPtr<WKStringRef> titleWK = adoptWK(WKNavigationDataCopyTitle(navigationData));
+ // HTTP method
+ WKRetainPtr<WKURLRequestRef> requestWK = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
+ WKRetainPtr<WKStringRef> methodWK = adoptWK(WKURLRequestCopyHTTPMethod(requestWK.get()));
+
+ // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
+ m_currentInvocation->outputText(String::format("WebView navigated to url \"%s\" with title \"%s\" with HTTP equivalent method \"%s\". The navigation was successful and was not a client redirect.\n",
+ toSTD(urlStringWK).c_str(), toSTD(titleWK).c_str(), toSTD(methodWK).c_str()));
+}
+
+void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
+}
+
+void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
+{
+ if (m_state != RunningTest)
+ return;
+
+ if (!m_shouldLogHistoryClientCallbacks)
+ return;
+
+ WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
+ WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
+
+ m_currentInvocation->outputText(String::format("WebView performed a client redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
+}
+
+void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
+}
+
+void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
+{
+ if (m_state != RunningTest)
+ return;
+
+ if (!m_shouldLogHistoryClientCallbacks)
+ return;
+
+ WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
+ WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
+
+ m_currentInvocation->outputText(String::format("WebView performed a server redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
+}
+
+void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
+}
+
+void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
+{
+ if (m_state != RunningTest)
+ return;
+
+ if (!m_shouldLogHistoryClientCallbacks)
+ return;
+
+ WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(URL));
+ m_currentInvocation->outputText(String::format("WebView updated the title for history URL \"%s\" to \"%s\".\n", toSTD(urlStringWK).c_str(), toSTD(title).c_str()));
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/TestController.h b/Tools/WebKitTestRunner/TestController.h
new file mode 100644
index 000000000..98689bf30
--- /dev/null
+++ b/Tools/WebKitTestRunner/TestController.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2010, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TestController_h
+#define TestController_h
+
+#include "GeolocationProviderMock.h"
+#include "WebNotificationProvider.h"
+#include "WorkQueueManager.h"
+#include <WebKit/WKRetainPtr.h>
+#include <string>
+#include <vector>
+#include <wtf/Vector.h>
+
+namespace WTR {
+
+class TestInvocation;
+class PlatformWebView;
+class EventSenderProxy;
+struct ViewOptions;
+
+// FIXME: Rename this TestRunner?
+class TestController {
+public:
+ static TestController& singleton();
+
+ static const unsigned viewWidth;
+ static const unsigned viewHeight;
+
+ static const unsigned w3cSVGViewWidth;
+ static const unsigned w3cSVGViewHeight;
+
+ static const double shortTimeout;
+ static const double noTimeout;
+
+ TestController(int argc, const char* argv[]);
+ ~TestController();
+
+ bool verbose() const { return m_verbose; }
+
+ WKStringRef injectedBundlePath() { return m_injectedBundlePath.get(); }
+ WKStringRef testPluginDirectory() { return m_testPluginDirectory.get(); }
+
+ PlatformWebView* mainWebView() { return m_mainWebView.get(); }
+ WKContextRef context() { return m_context.get(); }
+
+ EventSenderProxy* eventSenderProxy() { return m_eventSenderProxy.get(); }
+
+ void ensureViewSupportsOptions(const ViewOptions&);
+ bool shouldUseRemoteLayerTree() const { return m_shouldUseRemoteLayerTree; }
+
+ // Runs the run loop until `done` is true or the timeout elapses.
+ bool useWaitToDumpWatchdogTimer() { return m_useWaitToDumpWatchdogTimer; }
+ void runUntil(bool& done, double timeoutSeconds);
+ void notifyDone();
+
+ bool shouldShowWebView() const { return m_shouldShowWebView; }
+
+ void configureViewForTest(const TestInvocation&);
+
+ bool beforeUnloadReturnValue() const { return m_beforeUnloadReturnValue; }
+ void setBeforeUnloadReturnValue(bool value) { m_beforeUnloadReturnValue = value; }
+
+ void simulateWebNotificationClick(uint64_t notificationID);
+
+ // Geolocation.
+ void setGeolocationPermission(bool);
+ void setMockGeolocationPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed);
+ void setMockGeolocationPositionUnavailableError(WKStringRef errorMessage);
+ void handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef);
+ bool isGeolocationProviderActive() const;
+
+ // MediaStream.
+ void setUserMediaPermission(bool);
+ void handleUserMediaPermissionRequest(WKUserMediaPermissionRequestRef);
+
+ // Policy delegate.
+ void setCustomPolicyDelegate(bool enabled, bool permissive);
+
+ // Page Visibility.
+ void setHidden(bool);
+
+ bool resetStateToConsistentValues();
+ void resetPreferencesToConsistentValues();
+
+ void terminateWebContentProcess();
+ void reattachPageToWebProcess();
+
+ static const char* webProcessName();
+ static const char* networkProcessName();
+
+ WorkQueueManager& workQueueManager() { return m_workQueueManager; }
+
+ void setHandlesAuthenticationChallenges(bool value) { m_handlesAuthenticationChallenges = value; }
+ void setAuthenticationUsername(String username) { m_authenticationUsername = username; }
+ void setAuthenticationPassword(String password) { m_authenticationPassword = password; }
+
+ void setBlockAllPlugins(bool shouldBlock) { m_shouldBlockAllPlugins = shouldBlock; }
+
+ void setShouldLogHistoryClientCallbacks(bool shouldLog) { m_shouldLogHistoryClientCallbacks = shouldLog; }
+
+ bool isCurrentInvocation(TestInvocation* invocation) const { return invocation == m_currentInvocation.get(); }
+
+private:
+ void initialize(int argc, const char* argv[]);
+ void createWebViewWithOptions(const ViewOptions&);
+ void run();
+
+ void runTestingServerLoop();
+ bool runTest(const char* pathOrURL);
+
+ void platformInitialize();
+ void platformDestroy();
+ void platformInitializeContext();
+ void platformResetPreferencesToConsistentValues();
+ void platformConfigureViewForTest(const TestInvocation&);
+ void platformWillRunTest(const TestInvocation&);
+ void platformRunUntil(bool& done, double timeout);
+ void platformDidCommitLoadForFrame(WKPageRef, WKFrameRef);
+ void initializeInjectedBundlePath();
+ void initializeTestPluginDirectory();
+
+ void updateWebViewSizeForTest(const TestInvocation&);
+ void updateWindowScaleForTest(PlatformWebView*, const TestInvocation&);
+ void updateLayoutTypeForTest(const TestInvocation&);
+
+ void decidePolicyForGeolocationPermissionRequestIfPossible();
+ void decidePolicyForUserMediaPermissionRequestIfPossible();
+
+ // WKContextInjectedBundleClient
+ static void didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef messageBody, const void*);
+ static void didReceiveSynchronousMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void*);
+
+ // WKPageInjectedBundleClient
+ static void didReceivePageMessageFromInjectedBundle(WKPageRef, WKStringRef messageName, WKTypeRef messageBody, const void*);
+ static void didReceiveSynchronousPageMessageFromInjectedBundle(WKPageRef, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void*);
+ void didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody);
+ WKRetainPtr<WKTypeRef> didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody);
+
+ void didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous);
+
+ // WKContextClient
+ static void networkProcessDidCrash(WKContextRef, const void*);
+ void networkProcessDidCrash();
+
+ // WKPageNavigationClient
+ static void didCommitNavigation(WKPageRef, WKNavigationRef, WKTypeRef userData, const void*);
+ void didCommitNavigation(WKPageRef, WKNavigationRef);
+
+ static void didFinishNavigation(WKPageRef, WKNavigationRef, WKTypeRef userData, const void*);
+ void didFinishNavigation(WKPageRef, WKNavigationRef);
+
+ static void processDidCrash(WKPageRef, const void* clientInfo);
+ void processDidCrash();
+
+ static WKPluginLoadPolicy decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo);
+ WKPluginLoadPolicy decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription);
+
+
+ static void decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef, const void*);
+ void decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef);
+
+ static void unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*);
+
+ static bool canAuthenticateAgainstProtectionSpace(WKPageRef, WKProtectionSpaceRef, const void *clientInfo);
+
+ static void didReceiveAuthenticationChallenge(WKPageRef, WKAuthenticationChallengeRef, const void *clientInfo);
+ void didReceiveAuthenticationChallenge(WKPageRef, WKAuthenticationChallengeRef);
+
+ static void decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef, WKFramePolicyListenerRef, WKTypeRef, const void*);
+ void decidePolicyForNavigationAction(WKFramePolicyListenerRef);
+
+ static void decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef, WKFramePolicyListenerRef, WKTypeRef, const void*);
+ void decidePolicyForNavigationResponse(WKNavigationResponseRef, WKFramePolicyListenerRef);
+
+ // WKContextHistoryClient
+ static void didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef, WKFrameRef, const void*);
+ void didNavigateWithNavigationData(WKNavigationDataRef, WKFrameRef);
+
+ static void didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef, const void*);
+ void didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef);
+
+ static void didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef, const void*);
+ void didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef);
+
+ static void didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef, WKFrameRef, const void*);
+ void didUpdateHistoryTitle(WKStringRef title, WKURLRef, WKFrameRef);
+
+ static WKPageRef createOtherPage(WKPageRef oldPage, WKURLRequestRef, WKDictionaryRef, WKEventModifiers, WKEventMouseButton, const void*);
+
+ static void runModal(WKPageRef, const void* clientInfo);
+ static void runModal(PlatformWebView*);
+
+ static const char* libraryPathForTesting();
+ static const char* platformLibraryPathForTesting();
+
+ std::unique_ptr<TestInvocation> m_currentInvocation;
+
+ bool m_verbose;
+ bool m_printSeparators;
+ bool m_usingServerMode;
+ bool m_gcBetweenTests;
+ bool m_shouldDumpPixelsForAllTests;
+ std::vector<std::string> m_paths;
+ std::vector<std::string> m_allowedHosts;
+ WKRetainPtr<WKStringRef> m_injectedBundlePath;
+ WKRetainPtr<WKStringRef> m_testPluginDirectory;
+
+ WebNotificationProvider m_webNotificationProvider;
+
+ std::unique_ptr<PlatformWebView> m_mainWebView;
+ WKRetainPtr<WKContextRef> m_context;
+ WKRetainPtr<WKPageGroupRef> m_pageGroup;
+
+ enum State {
+ Initial,
+ Resetting,
+ RunningTest
+ };
+ State m_state;
+ bool m_doneResetting;
+
+ bool m_useWaitToDumpWatchdogTimer;
+ bool m_forceNoTimeout;
+
+ bool m_didPrintWebProcessCrashedMessage;
+ bool m_shouldExitWhenWebProcessCrashes;
+
+ bool m_beforeUnloadReturnValue;
+
+ std::unique_ptr<GeolocationProviderMock> m_geolocationProvider;
+ Vector<WKRetainPtr<WKGeolocationPermissionRequestRef> > m_geolocationPermissionRequests;
+ bool m_isGeolocationPermissionSet;
+ bool m_isGeolocationPermissionAllowed;
+
+ Vector<WKRetainPtr<WKUserMediaPermissionRequestRef>> m_userMediaPermissionRequests;
+ bool m_isUserMediaPermissionSet;
+ bool m_isUserMediaPermissionAllowed;
+
+ bool m_policyDelegateEnabled;
+ bool m_policyDelegatePermissive;
+
+ bool m_handlesAuthenticationChallenges;
+ String m_authenticationUsername;
+ String m_authenticationPassword;
+
+ bool m_shouldBlockAllPlugins;
+
+ bool m_forceComplexText;
+ bool m_shouldUseAcceleratedDrawing;
+ bool m_shouldUseRemoteLayerTree;
+
+ bool m_shouldLogHistoryClientCallbacks;
+ bool m_shouldShowWebView;
+
+ std::unique_ptr<EventSenderProxy> m_eventSenderProxy;
+
+ WorkQueueManager m_workQueueManager;
+};
+
+} // namespace WTR
+
+#endif // TestController_h
diff --git a/Tools/WebKitTestRunner/TestInvocation.cpp b/Tools/WebKitTestRunner/TestInvocation.cpp
new file mode 100644
index 000000000..7a36d49e6
--- /dev/null
+++ b/Tools/WebKitTestRunner/TestInvocation.cpp
@@ -0,0 +1,683 @@
+/*
+ * Copyright (C) 2010, 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestInvocation.h"
+
+#include "PlatformWebView.h"
+#include "StringFunctions.h"
+#include "TestController.h"
+#include <WebKit/WKContextPrivate.h>
+#include <WebKit/WKCookieManager.h>
+#include <WebKit/WKData.h>
+#include <WebKit/WKDictionary.h>
+#include <WebKit/WKInspector.h>
+#include <WebKit/WKPagePrivate.h>
+#include <WebKit/WKRetainPtr.h>
+#include <climits>
+#include <cstdio>
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/CString.h>
+
+#if PLATFORM(MAC) && !PLATFORM(IOS)
+#include <Carbon/Carbon.h>
+#endif
+
+#if PLATFORM(COCOA)
+#include <WebKit/WKPagePrivateMac.h>
+#endif
+
+#include <unistd.h> // For getcwd.
+
+using namespace WebKit;
+using namespace std;
+
+namespace WTR {
+
+static WKURLRef createWKURL(const char* pathOrURL)
+{
+ if (strstr(pathOrURL, "http://") || strstr(pathOrURL, "https://") || strstr(pathOrURL, "file://"))
+ return WKURLCreateWithUTF8CString(pathOrURL);
+
+ // Creating from filesytem path.
+ size_t length = strlen(pathOrURL);
+ if (!length)
+ return 0;
+
+ const char separator = '/';
+ bool isAbsolutePath = pathOrURL[0] == separator;
+ const char* filePrefix = "file://";
+ static const size_t prefixLength = strlen(filePrefix);
+
+ std::unique_ptr<char[]> buffer;
+ if (isAbsolutePath) {
+ buffer = std::make_unique<char[]>(prefixLength + length + 1);
+ strcpy(buffer.get(), filePrefix);
+ strcpy(buffer.get() + prefixLength, pathOrURL);
+ } else {
+ buffer = std::make_unique<char[]>(prefixLength + PATH_MAX + length + 2); // 1 for the separator
+ strcpy(buffer.get(), filePrefix);
+ if (!getcwd(buffer.get() + prefixLength, PATH_MAX))
+ return 0;
+ size_t numCharacters = strlen(buffer.get());
+ buffer[numCharacters] = separator;
+ strcpy(buffer.get() + numCharacters + 1, pathOrURL);
+ }
+
+ return WKURLCreateWithUTF8CString(buffer.get());
+}
+
+TestInvocation::TestInvocation(const std::string& pathOrURL)
+ : m_url(AdoptWK, createWKURL(pathOrURL.c_str()))
+ , m_dumpPixels(false)
+ , m_timeout(0)
+ , m_gotInitialResponse(false)
+ , m_gotFinalMessage(false)
+ , m_gotRepaint(false)
+ , m_error(false)
+ , m_webProcessIsUnresponsive(false)
+{
+ WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(m_url.get()));
+
+ size_t stringLength = WKStringGetLength(urlString.get());
+
+ Vector<char> urlVector;
+ urlVector.resize(stringLength + 1);
+
+ WKStringGetUTF8CString(urlString.get(), urlVector.data(), stringLength + 1);
+
+ m_urlString = String(urlVector.data(), stringLength);
+}
+
+TestInvocation::~TestInvocation()
+{
+}
+
+WKURLRef TestInvocation::url() const
+{
+ return m_url.get();
+}
+
+bool TestInvocation::urlContains(const char* searchString) const
+{
+ return m_urlString.contains(searchString, false);
+}
+
+void TestInvocation::setIsPixelTest(const std::string& expectedPixelHash)
+{
+ m_dumpPixels = true;
+ m_expectedPixelHash = expectedPixelHash;
+}
+
+bool TestInvocation::shouldLogFrameLoadDelegates()
+{
+ return urlContains("loading/");
+}
+
+bool TestInvocation::shouldLogHistoryClientCallbacks()
+{
+ return urlContains("globalhistory/");
+}
+
+void TestInvocation::invoke()
+{
+ TestController::singleton().configureViewForTest(*this);
+
+ WKPageSetAddsVisitedLinks(TestController::singleton().mainWebView()->page(), false);
+
+ m_textOutput.clear();
+
+ TestController::singleton().setShouldLogHistoryClientCallbacks(shouldLogHistoryClientCallbacks());
+
+ WKCookieManagerSetHTTPCookieAcceptPolicy(WKContextGetCookieManager(TestController::singleton().context()), kWKHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain);
+
+ // FIXME: We should clear out visited links here.
+
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("BeginTest"));
+ WKRetainPtr<WKMutableDictionaryRef> beginTestMessageBody = adoptWK(WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> dumpFrameLoadDelegatesKey = adoptWK(WKStringCreateWithUTF8CString("DumpFrameLoadDelegates"));
+ WKRetainPtr<WKBooleanRef> dumpFrameLoadDelegatesValue = adoptWK(WKBooleanCreate(shouldLogFrameLoadDelegates()));
+ WKDictionarySetItem(beginTestMessageBody.get(), dumpFrameLoadDelegatesKey.get(), dumpFrameLoadDelegatesValue.get());
+
+ WKRetainPtr<WKStringRef> dumpPixelsKey = adoptWK(WKStringCreateWithUTF8CString("DumpPixels"));
+ WKRetainPtr<WKBooleanRef> dumpPixelsValue = adoptWK(WKBooleanCreate(m_dumpPixels));
+ WKDictionarySetItem(beginTestMessageBody.get(), dumpPixelsKey.get(), dumpPixelsValue.get());
+
+ WKRetainPtr<WKStringRef> useWaitToDumpWatchdogTimerKey = adoptWK(WKStringCreateWithUTF8CString("UseWaitToDumpWatchdogTimer"));
+ WKRetainPtr<WKBooleanRef> useWaitToDumpWatchdogTimerValue = adoptWK(WKBooleanCreate(TestController::singleton().useWaitToDumpWatchdogTimer()));
+ WKDictionarySetItem(beginTestMessageBody.get(), useWaitToDumpWatchdogTimerKey.get(), useWaitToDumpWatchdogTimerValue.get());
+
+ WKRetainPtr<WKStringRef> timeoutKey = adoptWK(WKStringCreateWithUTF8CString("Timeout"));
+ WKRetainPtr<WKUInt64Ref> timeoutValue = adoptWK(WKUInt64Create(m_timeout));
+ WKDictionarySetItem(beginTestMessageBody.get(), timeoutKey.get(), timeoutValue.get());
+
+ WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), beginTestMessageBody.get());
+
+ bool shouldOpenExternalURLs = false;
+
+ TestController::singleton().runUntil(m_gotInitialResponse, TestController::shortTimeout);
+ if (!m_gotInitialResponse) {
+ m_errorMessage = "Timed out waiting for initial response from web process\n";
+ m_webProcessIsUnresponsive = true;
+ goto end;
+ }
+ if (m_error)
+ goto end;
+
+ WKPageLoadURLWithShouldOpenExternalURLsPolicy(TestController::singleton().mainWebView()->page(), m_url.get(), shouldOpenExternalURLs);
+
+ TestController::singleton().runUntil(m_gotFinalMessage, TestController::noTimeout);
+ if (m_error)
+ goto end;
+
+ dumpResults();
+
+end:
+#if !PLATFORM(IOS)
+ if (m_gotInitialResponse)
+ WKInspectorClose(WKPageGetInspector(TestController::singleton().mainWebView()->page()));
+#endif // !PLATFORM(IOS)
+
+ if (m_webProcessIsUnresponsive)
+ dumpWebProcessUnresponsiveness();
+ else if (!TestController::singleton().resetStateToConsistentValues()) {
+ // The process froze while loading about:blank, let's start a fresh one.
+ // It would be nice to report that the previous test froze after dumping results, but we have no way to do that.
+ TestController::singleton().terminateWebContentProcess();
+ // Make sure that we have a process, as invoke() will need one to send bundle messages for the next test.
+ TestController::singleton().reattachPageToWebProcess();
+ }
+}
+
+void TestInvocation::dumpWebProcessUnresponsiveness()
+{
+ dumpWebProcessUnresponsiveness(m_errorMessage.c_str());
+}
+
+void TestInvocation::dumpWebProcessUnresponsiveness(const char* errorMessage)
+{
+ char errorMessageToStderr[1024];
+#if PLATFORM(COCOA)
+ pid_t pid = WKPageGetProcessIdentifier(TestController::singleton().mainWebView()->page());
+ sprintf(errorMessageToStderr, "#PROCESS UNRESPONSIVE - %s (pid %ld)\n", TestController::webProcessName(), static_cast<long>(pid));
+#else
+ sprintf(errorMessageToStderr, "#PROCESS UNRESPONSIVE - %s", TestController::webProcessName());
+#endif
+
+ dump(errorMessage, errorMessageToStderr, true);
+}
+
+void TestInvocation::dump(const char* textToStdout, const char* textToStderr, bool seenError)
+{
+ printf("Content-Type: text/plain\n");
+ if (textToStdout)
+ fputs(textToStdout, stdout);
+ if (textToStderr)
+ fputs(textToStderr, stderr);
+
+ fputs("#EOF\n", stdout);
+ fputs("#EOF\n", stderr);
+ if (seenError)
+ fputs("#EOF\n", stdout);
+ fflush(stdout);
+ fflush(stderr);
+}
+
+void TestInvocation::forceRepaintDoneCallback(WKErrorRef error, void* context)
+{
+ // The context may not be valid any more, e.g. if WebKit is invalidating callbacks at process exit.
+ if (error)
+ return;
+
+ TestInvocation* testInvocation = static_cast<TestInvocation*>(context);
+ RELEASE_ASSERT(TestController::singleton().isCurrentInvocation(testInvocation));
+
+ testInvocation->m_gotRepaint = true;
+ TestController::singleton().notifyDone();
+}
+
+void TestInvocation::dumpResults()
+{
+ if (m_textOutput.length() || !m_audioResult)
+ dump(m_textOutput.toString().utf8().data());
+ else
+ dumpAudio(m_audioResult.get());
+
+ if (m_dumpPixels && m_pixelResult) {
+ if (PlatformWebView::windowSnapshotEnabled()) {
+ m_gotRepaint = false;
+ WKPageForceRepaint(TestController::singleton().mainWebView()->page(), this, TestInvocation::forceRepaintDoneCallback);
+ TestController::singleton().runUntil(m_gotRepaint, TestController::shortTimeout);
+ if (!m_gotRepaint) {
+ m_errorMessage = "Timed out waiting for pre-pixel dump repaint\n";
+ m_webProcessIsUnresponsive = true;
+ return;
+ }
+ }
+ dumpPixelsAndCompareWithExpected(m_pixelResult.get(), m_repaintRects.get());
+ }
+
+ fputs("#EOF\n", stdout);
+ fflush(stdout);
+ fflush(stderr);
+}
+
+void TestInvocation::dumpAudio(WKDataRef audioData)
+{
+ size_t length = WKDataGetSize(audioData);
+ if (!length)
+ return;
+
+ const unsigned char* data = WKDataGetBytes(audioData);
+
+ printf("Content-Type: audio/wav\n");
+ printf("Content-Length: %lu\n", static_cast<unsigned long>(length));
+
+ fwrite(data, 1, length, stdout);
+ printf("#EOF\n");
+ fprintf(stderr, "#EOF\n");
+}
+
+bool TestInvocation::compareActualHashToExpectedAndDumpResults(const char actualHash[33])
+{
+ // Compute the hash of the bitmap context pixels
+ fprintf(stdout, "\nActualHash: %s\n", actualHash);
+
+ if (!m_expectedPixelHash.length())
+ return false;
+
+ ASSERT(m_expectedPixelHash.length() == 32);
+ fprintf(stdout, "\nExpectedHash: %s\n", m_expectedPixelHash.c_str());
+
+ // FIXME: Do case insensitive compare.
+ return m_expectedPixelHash == actualHash;
+}
+
+void TestInvocation::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
+{
+ if (WKStringIsEqualToUTF8CString(messageName, "Error")) {
+ // Set all states to true to stop spinning the runloop.
+ m_gotInitialResponse = true;
+ m_gotFinalMessage = true;
+ m_error = true;
+ m_errorMessage = "FAIL\n";
+ TestController::singleton().notifyDone();
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "Ack")) {
+ ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
+ WKStringRef messageBodyString = static_cast<WKStringRef>(messageBody);
+ if (WKStringIsEqualToUTF8CString(messageBodyString, "BeginTest")) {
+ m_gotInitialResponse = true;
+ TestController::singleton().notifyDone();
+ return;
+ }
+
+ ASSERT_NOT_REACHED();
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "Done")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> pixelResultKey = adoptWK(WKStringCreateWithUTF8CString("PixelResult"));
+ m_pixelResult = static_cast<WKImageRef>(WKDictionaryGetItemForKey(messageBodyDictionary, pixelResultKey.get()));
+ ASSERT(!m_pixelResult || m_dumpPixels);
+
+ WKRetainPtr<WKStringRef> repaintRectsKey = adoptWK(WKStringCreateWithUTF8CString("RepaintRects"));
+ m_repaintRects = static_cast<WKArrayRef>(WKDictionaryGetItemForKey(messageBodyDictionary, repaintRectsKey.get()));
+
+ WKRetainPtr<WKStringRef> audioResultKey = adoptWK(WKStringCreateWithUTF8CString("AudioResult"));
+ m_audioResult = static_cast<WKDataRef>(WKDictionaryGetItemForKey(messageBodyDictionary, audioResultKey.get()));
+
+ m_gotFinalMessage = true;
+ TestController::singleton().notifyDone();
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "TextOutput")) {
+ ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
+ WKStringRef textOutput = static_cast<WKStringRef>(messageBody);
+ m_textOutput.append(toWTFString(textOutput));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "BeforeUnloadReturnValue")) {
+ ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
+ WKBooleanRef beforeUnloadReturnValue = static_cast<WKBooleanRef>(messageBody);
+ TestController::singleton().setBeforeUnloadReturnValue(WKBooleanGetValue(beforeUnloadReturnValue));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "AddChromeInputField")) {
+ TestController::singleton().mainWebView()->addChromeInputField();
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallAddChromeInputFieldCallback"));
+ WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "RemoveChromeInputField")) {
+ TestController::singleton().mainWebView()->removeChromeInputField();
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallRemoveChromeInputFieldCallback"));
+ WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "FocusWebView")) {
+ TestController::singleton().mainWebView()->makeWebViewFirstResponder();
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallFocusWebViewCallback"));
+ WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetBackingScaleFactor")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDoubleGetTypeID());
+ double backingScaleFactor = WKDoubleGetValue(static_cast<WKDoubleRef>(messageBody));
+ WKPageSetCustomBackingScaleFactor(TestController::singleton().mainWebView()->page(), backingScaleFactor);
+
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallSetBackingScaleFactorCallback"));
+ WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SimulateWebNotificationClick")) {
+ ASSERT(WKGetTypeID(messageBody) == WKUInt64GetTypeID());
+ uint64_t notificationID = WKUInt64GetValue(static_cast<WKUInt64Ref>(messageBody));
+ TestController::singleton().simulateWebNotificationClick(notificationID);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetAddsVisitedLinks")) {
+ ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
+ WKBooleanRef enabledWK = static_cast<WKBooleanRef>(messageBody);
+ WKPageSetAddsVisitedLinks(TestController::singleton().mainWebView()->page(), WKBooleanGetValue(enabledWK));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetGeolocationPermission")) {
+ ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
+ WKBooleanRef enabledWK = static_cast<WKBooleanRef>(messageBody);
+ TestController::singleton().setGeolocationPermission(WKBooleanGetValue(enabledWK));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetMockGeolocationPosition")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> latitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("latitude"));
+ WKDoubleRef latitudeWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, latitudeKeyWK.get()));
+ double latitude = WKDoubleGetValue(latitudeWK);
+
+ WKRetainPtr<WKStringRef> longitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("longitude"));
+ WKDoubleRef longitudeWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, longitudeKeyWK.get()));
+ double longitude = WKDoubleGetValue(longitudeWK);
+
+ WKRetainPtr<WKStringRef> accuracyKeyWK(AdoptWK, WKStringCreateWithUTF8CString("accuracy"));
+ WKDoubleRef accuracyWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, accuracyKeyWK.get()));
+ double accuracy = WKDoubleGetValue(accuracyWK);
+
+ WKRetainPtr<WKStringRef> providesAltitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesAltitude"));
+ WKBooleanRef providesAltitudeWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, providesAltitudeKeyWK.get()));
+ bool providesAltitude = WKBooleanGetValue(providesAltitudeWK);
+
+ WKRetainPtr<WKStringRef> altitudeKeyWK(AdoptWK, WKStringCreateWithUTF8CString("altitude"));
+ WKDoubleRef altitudeWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, altitudeKeyWK.get()));
+ double altitude = WKDoubleGetValue(altitudeWK);
+
+ WKRetainPtr<WKStringRef> providesAltitudeAccuracyKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesAltitudeAccuracy"));
+ WKBooleanRef providesAltitudeAccuracyWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, providesAltitudeAccuracyKeyWK.get()));
+ bool providesAltitudeAccuracy = WKBooleanGetValue(providesAltitudeAccuracyWK);
+
+ WKRetainPtr<WKStringRef> altitudeAccuracyKeyWK(AdoptWK, WKStringCreateWithUTF8CString("altitudeAccuracy"));
+ WKDoubleRef altitudeAccuracyWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, altitudeAccuracyKeyWK.get()));
+ double altitudeAccuracy = WKDoubleGetValue(altitudeAccuracyWK);
+
+ WKRetainPtr<WKStringRef> providesHeadingKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesHeading"));
+ WKBooleanRef providesHeadingWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, providesHeadingKeyWK.get()));
+ bool providesHeading = WKBooleanGetValue(providesHeadingWK);
+
+ WKRetainPtr<WKStringRef> headingKeyWK(AdoptWK, WKStringCreateWithUTF8CString("heading"));
+ WKDoubleRef headingWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, headingKeyWK.get()));
+ double heading = WKDoubleGetValue(headingWK);
+
+ WKRetainPtr<WKStringRef> providesSpeedKeyWK(AdoptWK, WKStringCreateWithUTF8CString("providesSpeed"));
+ WKBooleanRef providesSpeedWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, providesSpeedKeyWK.get()));
+ bool providesSpeed = WKBooleanGetValue(providesSpeedWK);
+
+ WKRetainPtr<WKStringRef> speedKeyWK(AdoptWK, WKStringCreateWithUTF8CString("speed"));
+ WKDoubleRef speedWK = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, speedKeyWK.get()));
+ double speed = WKDoubleGetValue(speedWK);
+
+ TestController::singleton().setMockGeolocationPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetMockGeolocationPositionUnavailableError")) {
+ ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
+ WKStringRef errorMessage = static_cast<WKStringRef>(messageBody);
+ TestController::singleton().setMockGeolocationPositionUnavailableError(errorMessage);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetUserMediaPermission")) {
+ ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
+ WKBooleanRef enabledWK = static_cast<WKBooleanRef>(messageBody);
+ TestController::singleton().setUserMediaPermission(WKBooleanGetValue(enabledWK));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetCacheModel")) {
+ ASSERT(WKGetTypeID(messageBody) == WKUInt64GetTypeID());
+ uint64_t model = WKUInt64GetValue(static_cast<WKUInt64Ref>(messageBody));
+ WKContextSetCacheModel(TestController::singleton().context(), model);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetCustomPolicyDelegate")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> enabledKeyWK(AdoptWK, WKStringCreateWithUTF8CString("enabled"));
+ WKBooleanRef enabledWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, enabledKeyWK.get()));
+ bool enabled = WKBooleanGetValue(enabledWK);
+
+ WKRetainPtr<WKStringRef> permissiveKeyWK(AdoptWK, WKStringCreateWithUTF8CString("permissive"));
+ WKBooleanRef permissiveWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, permissiveKeyWK.get()));
+ bool permissive = WKBooleanGetValue(permissiveWK);
+
+ TestController::singleton().setCustomPolicyDelegate(enabled, permissive);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetHidden")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> isInitialKeyWK(AdoptWK, WKStringCreateWithUTF8CString("hidden"));
+ WKBooleanRef hiddenWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, isInitialKeyWK.get()));
+ bool hidden = WKBooleanGetValue(hiddenWK);
+
+ TestController::singleton().setHidden(hidden);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "ProcessWorkQueue")) {
+ if (TestController::singleton().workQueueManager().processWorkQueue()) {
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("WorkQueueProcessedCallback"));
+ WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
+ }
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "QueueBackNavigation")) {
+ ASSERT(WKGetTypeID(messageBody) == WKUInt64GetTypeID());
+ uint64_t stepCount = WKUInt64GetValue(static_cast<WKUInt64Ref>(messageBody));
+ TestController::singleton().workQueueManager().queueBackNavigation(stepCount);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "QueueForwardNavigation")) {
+ ASSERT(WKGetTypeID(messageBody) == WKUInt64GetTypeID());
+ uint64_t stepCount = WKUInt64GetValue(static_cast<WKUInt64Ref>(messageBody));
+ TestController::singleton().workQueueManager().queueForwardNavigation(stepCount);
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "QueueLoad")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef loadDataDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> urlKey(AdoptWK, WKStringCreateWithUTF8CString("url"));
+ WKStringRef urlWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(loadDataDictionary, urlKey.get()));
+
+ WKRetainPtr<WKStringRef> targetKey(AdoptWK, WKStringCreateWithUTF8CString("target"));
+ WKStringRef targetWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(loadDataDictionary, targetKey.get()));
+
+ WKRetainPtr<WKStringRef> shouldOpenExternalURLsKey(AdoptWK, WKStringCreateWithUTF8CString("shouldOpenExternalURLs"));
+ WKBooleanRef shouldOpenExternalURLsValueWK = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(loadDataDictionary, shouldOpenExternalURLsKey.get()));
+
+ TestController::singleton().workQueueManager().queueLoad(toWTFString(urlWK), toWTFString(targetWK), WKBooleanGetValue(shouldOpenExternalURLsValueWK));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "QueueLoadHTMLString")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef loadDataDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> contentKey(AdoptWK, WKStringCreateWithUTF8CString("content"));
+ WKStringRef contentWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(loadDataDictionary, contentKey.get()));
+
+ WKRetainPtr<WKStringRef> baseURLKey(AdoptWK, WKStringCreateWithUTF8CString("baseURL"));
+ WKStringRef baseURLWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(loadDataDictionary, baseURLKey.get()));
+
+ WKRetainPtr<WKStringRef> unreachableURLKey(AdoptWK, WKStringCreateWithUTF8CString("unreachableURL"));
+ WKStringRef unreachableURLWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(loadDataDictionary, unreachableURLKey.get()));
+
+ TestController::singleton().workQueueManager().queueLoadHTMLString(toWTFString(contentWK), baseURLWK ? toWTFString(baseURLWK) : String(), unreachableURLWK ? toWTFString(unreachableURLWK) : String());
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "QueueReload")) {
+ TestController::singleton().workQueueManager().queueReload();
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "QueueLoadingScript")) {
+ ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
+ WKStringRef script = static_cast<WKStringRef>(messageBody);
+ TestController::singleton().workQueueManager().queueLoadingScript(toWTFString(script));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "QueueNonLoadingScript")) {
+ ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
+ WKStringRef script = static_cast<WKStringRef>(messageBody);
+ TestController::singleton().workQueueManager().queueNonLoadingScript(toWTFString(script));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetHandlesAuthenticationChallenge")) {
+ ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
+ WKBooleanRef value = static_cast<WKBooleanRef>(messageBody);
+ TestController::singleton().setHandlesAuthenticationChallenges(WKBooleanGetValue(value));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetAuthenticationUsername")) {
+ ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
+ WKStringRef username = static_cast<WKStringRef>(messageBody);
+ TestController::singleton().setAuthenticationUsername(toWTFString(username));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetAuthenticationPassword")) {
+ ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
+ WKStringRef password = static_cast<WKStringRef>(messageBody);
+ TestController::singleton().setAuthenticationPassword(toWTFString(password));
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetBlockAllPlugins")) {
+ ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
+ WKBooleanRef shouldBlock = static_cast<WKBooleanRef>(messageBody);
+ TestController::singleton().setBlockAllPlugins(WKBooleanGetValue(shouldBlock));
+ return;
+ }
+
+ ASSERT_NOT_REACHED();
+}
+
+WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
+{
+ if (WKStringIsEqualToUTF8CString(messageName, "SetWindowIsKey")) {
+ ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
+ WKBooleanRef isKeyValue = static_cast<WKBooleanRef>(messageBody);
+ TestController::singleton().mainWebView()->setWindowIsKey(WKBooleanGetValue(isKeyValue));
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "IsGeolocationClientActive")) {
+ bool isActive = TestController::singleton().isGeolocationProviderActive();
+ WKRetainPtr<WKTypeRef> result(AdoptWK, WKBooleanCreate(isActive));
+ return result;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "IsWorkQueueEmpty")) {
+ bool isEmpty = TestController::singleton().workQueueManager().isWorkQueueEmpty();
+ WKRetainPtr<WKTypeRef> result(AdoptWK, WKBooleanCreate(isEmpty));
+ return result;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SecureEventInputIsEnabled")) {
+#if PLATFORM(MAC) && !PLATFORM(IOS)
+ WKRetainPtr<WKBooleanRef> result(AdoptWK, WKBooleanCreate(IsSecureEventInputEnabled()));
+#else
+ WKRetainPtr<WKBooleanRef> result(AdoptWK, WKBooleanCreate(false));
+#endif
+ return result;
+ }
+
+ if (WKStringIsEqualToUTF8CString(messageName, "SetAlwaysAcceptCookies")) {
+ WKBooleanRef accept = static_cast<WKBooleanRef>(messageBody);
+ WKHTTPCookieAcceptPolicy policy = WKBooleanGetValue(accept) ? kWKHTTPCookieAcceptPolicyAlways : kWKHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain;
+ // FIXME: This updates the policy in WebProcess and in NetworkProcess asynchronously, which might break some tests' expectations.
+ WKCookieManagerSetHTTPCookieAcceptPolicy(WKContextGetCookieManager(TestController::singleton().context()), policy);
+ return 0;
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+void TestInvocation::outputText(const WTF::String& text)
+{
+ m_textOutput.append(text);
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/TestInvocation.h b/Tools/WebKitTestRunner/TestInvocation.h
new file mode 100644
index 000000000..ff760e73e
--- /dev/null
+++ b/Tools/WebKitTestRunner/TestInvocation.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TestInvocation_h
+#define TestInvocation_h
+
+#include <WebKit/WKRetainPtr.h>
+#include <string>
+#include <wtf/Noncopyable.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WTR {
+
+class TestInvocation {
+ WTF_MAKE_NONCOPYABLE(TestInvocation);
+public:
+ explicit TestInvocation(const std::string& pathOrURL);
+ ~TestInvocation();
+
+ WKURLRef url() const;
+ bool urlContains(const char*) const;
+
+ void setIsPixelTest(const std::string& expectedPixelHash);
+
+ void setCustomTimeout(int duration) { m_timeout = duration; }
+ int customTimeout() const { return m_timeout; }
+
+ void invoke();
+ void didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody);
+ WKRetainPtr<WKTypeRef> didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody);
+
+ void dumpWebProcessUnresponsiveness();
+ static void dumpWebProcessUnresponsiveness(const char* errorMessage);
+ void outputText(const WTF::String&);
+private:
+ void dumpResults();
+ static void dump(const char* textToStdout, const char* textToStderr = 0, bool seenError = false);
+ void dumpPixelsAndCompareWithExpected(WKImageRef, WKArrayRef repaintRects);
+ void dumpAudio(WKDataRef);
+ bool compareActualHashToExpectedAndDumpResults(const char[33]);
+
+ static void forceRepaintDoneCallback(WKErrorRef, void* context);
+
+ bool shouldLogFrameLoadDelegates();
+ bool shouldLogHistoryClientCallbacks();
+
+ WKRetainPtr<WKURLRef> m_url;
+ WTF::String m_urlString;
+
+ bool m_dumpPixels;
+ std::string m_expectedPixelHash;
+
+ int m_timeout;
+
+ // Invocation state
+ bool m_gotInitialResponse;
+ bool m_gotFinalMessage;
+ bool m_gotRepaint;
+ bool m_error;
+
+ StringBuilder m_textOutput;
+ WKRetainPtr<WKDataRef> m_audioResult;
+ WKRetainPtr<WKImageRef> m_pixelResult;
+ WKRetainPtr<WKArrayRef> m_repaintRects;
+ std::string m_errorMessage;
+ bool m_webProcessIsUnresponsive;
+
+};
+
+} // namespace WTR
+
+#endif // TestInvocation_h
diff --git a/Tools/WebKitTestRunner/ViewOptions.h b/Tools/WebKitTestRunner/ViewOptions.h
new file mode 100644
index 000000000..f460af7fe
--- /dev/null
+++ b/Tools/WebKitTestRunner/ViewOptions.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ViewOptions_h
+#define ViewOptions_h
+
+namespace WTR {
+
+struct ViewOptions {
+ bool useThreadedScrolling { false };
+ bool useRemoteLayerTree { false };
+ bool shouldShowWebView { false };
+ bool useTiledDrawing { false };
+
+ bool useFixedLayout { false };
+};
+
+}
+
+#endif // ViewOptions_h
diff --git a/Tools/WebKitTestRunner/WebKitTestRunner.sln b/Tools/WebKitTestRunner/WebKitTestRunner.sln
new file mode 100644
index 000000000..9d11a8ed9
--- /dev/null
+++ b/Tools/WebKitTestRunner/WebKitTestRunner.sln
@@ -0,0 +1,104 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WebKitTestRunner", "win\WebKitTestRunner.vcproj", "{3B99669B-1817-443B-BCBE-835580146668}"
+ ProjectSection(ProjectDependencies) = postProject
+ {CBC3391C-F060-4BF5-A66E-81404168816B} = {CBC3391C-F060-4BF5-A66E-81404168816B}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InjectedBundleGenerated", "win\InjectedBundleGenerated.vcproj", "{4343BC0B-A2E0-4B48-8277-F33CFBFA83CD}"
+ ProjectSection(ProjectDependencies) = postProject
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C} = {C0737398-3565-439E-A2B8-AB2BE4D5430C}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageDiff", "..\DumpRenderTree\win\ImageDiff.vcproj", "{59CC0547-70AC-499C-9B19-EC01C6F61137}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InjectedBundle", "win\InjectedBundle.vcproj", "{CBC3391C-F060-4BF5-A66E-81404168816B}"
+ ProjectSection(ProjectDependencies) = postProject
+ {4343BC0B-A2E0-4B48-8277-F33CFBFA83CD} = {4343BC0B-A2E0-4B48-8277-F33CFBFA83CD}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestNetscapePlugin", "..\DumpRenderTree\TestNetscapePlugIn\win\TestNetscapePlugin.vcproj", "{C0737398-3565-439E-A2B8-AB2BE4D5430C}"
+ ProjectSection(ProjectDependencies) = postProject
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017} = {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WebKitTestRunnerLauncher", "win\WebKitTestRunnerLauncher.vcproj", "{C13FA6EF-B531-4BAD-9A23-18E2BEB8B040}"
+ ProjectSection(ProjectDependencies) = postProject
+ {3B99669B-1817-443B-BCBE-835580146668} = {3B99669B-1817-443B-BCBE-835580146668}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageDiffLauncher", "..\DumpRenderTree\win\ImageDiffLauncher.vcproj", "{DD7949B6-F2B4-47C2-9C42-E21E84CB1017}"
+ ProjectSection(ProjectDependencies) = postProject
+ {59CC0547-70AC-499C-9B19-EC01C6F61137} = {59CC0547-70AC-499C-9B19-EC01C6F61137}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug_All|Win32 = Debug_All|Win32
+ Debug|Win32 = Debug|Win32
+ Production|Win32 = Production|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3B99669B-1817-443B-BCBE-835580146668}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {3B99669B-1817-443B-BCBE-835580146668}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {3B99669B-1817-443B-BCBE-835580146668}.Debug|Win32.ActiveCfg = Debug|Win32
+ {3B99669B-1817-443B-BCBE-835580146668}.Debug|Win32.Build.0 = Debug|Win32
+ {3B99669B-1817-443B-BCBE-835580146668}.Production|Win32.ActiveCfg = Production|Win32
+ {3B99669B-1817-443B-BCBE-835580146668}.Production|Win32.Build.0 = Production|Win32
+ {3B99669B-1817-443B-BCBE-835580146668}.Release|Win32.ActiveCfg = Release|Win32
+ {3B99669B-1817-443B-BCBE-835580146668}.Release|Win32.Build.0 = Release|Win32
+ {4343BC0B-A2E0-4B48-8277-F33CFBFA83CD}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {4343BC0B-A2E0-4B48-8277-F33CFBFA83CD}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {4343BC0B-A2E0-4B48-8277-F33CFBFA83CD}.Debug|Win32.ActiveCfg = Debug|Win32
+ {4343BC0B-A2E0-4B48-8277-F33CFBFA83CD}.Debug|Win32.Build.0 = Debug|Win32
+ {4343BC0B-A2E0-4B48-8277-F33CFBFA83CD}.Production|Win32.ActiveCfg = Production|Win32
+ {4343BC0B-A2E0-4B48-8277-F33CFBFA83CD}.Production|Win32.Build.0 = Production|Win32
+ {4343BC0B-A2E0-4B48-8277-F33CFBFA83CD}.Release|Win32.ActiveCfg = Release|Win32
+ {4343BC0B-A2E0-4B48-8277-F33CFBFA83CD}.Release|Win32.Build.0 = Release|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug|Win32.ActiveCfg = Debug|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug|Win32.Build.0 = Debug|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Production|Win32.ActiveCfg = Production|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Production|Win32.Build.0 = Production|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Release|Win32.ActiveCfg = Release|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Release|Win32.Build.0 = Release|Win32
+ {CBC3391C-F060-4BF5-A66E-81404168816B}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {CBC3391C-F060-4BF5-A66E-81404168816B}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {CBC3391C-F060-4BF5-A66E-81404168816B}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CBC3391C-F060-4BF5-A66E-81404168816B}.Debug|Win32.Build.0 = Debug|Win32
+ {CBC3391C-F060-4BF5-A66E-81404168816B}.Production|Win32.ActiveCfg = Production|Win32
+ {CBC3391C-F060-4BF5-A66E-81404168816B}.Production|Win32.Build.0 = Production|Win32
+ {CBC3391C-F060-4BF5-A66E-81404168816B}.Release|Win32.ActiveCfg = Release|Win32
+ {CBC3391C-F060-4BF5-A66E-81404168816B}.Release|Win32.Build.0 = Release|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug|Win32.Build.0 = Debug|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Production|Win32.ActiveCfg = Production|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Production|Win32.Build.0 = Production|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Release|Win32.ActiveCfg = Release|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Release|Win32.Build.0 = Release|Win32
+ {C13FA6EF-B531-4BAD-9A23-18E2BEB8B040}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {C13FA6EF-B531-4BAD-9A23-18E2BEB8B040}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {C13FA6EF-B531-4BAD-9A23-18E2BEB8B040}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C13FA6EF-B531-4BAD-9A23-18E2BEB8B040}.Debug|Win32.Build.0 = Debug|Win32
+ {C13FA6EF-B531-4BAD-9A23-18E2BEB8B040}.Production|Win32.ActiveCfg = Production|Win32
+ {C13FA6EF-B531-4BAD-9A23-18E2BEB8B040}.Production|Win32.Build.0 = Production|Win32
+ {C13FA6EF-B531-4BAD-9A23-18E2BEB8B040}.Release|Win32.ActiveCfg = Release|Win32
+ {C13FA6EF-B531-4BAD-9A23-18E2BEB8B040}.Release|Win32.Build.0 = Release|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Debug_All|Win32.Build.0 = Debug_All|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Debug|Win32.Build.0 = Debug|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Production|Win32.ActiveCfg = Production|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Production|Win32.Build.0 = Production|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Release|Win32.ActiveCfg = Release|Win32
+ {DD7949B6-F2B4-47C2-9C42-E21E84CB1017}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Tools/WebKitTestRunner/WebKitTestRunnerApp/AppDelegate.h b/Tools/WebKitTestRunner/WebKitTestRunnerApp/AppDelegate.h
new file mode 100644
index 000000000..27752908b
--- /dev/null
+++ b/Tools/WebKitTestRunner/WebKitTestRunnerApp/AppDelegate.h
@@ -0,0 +1,15 @@
+//
+// AppDelegate.h
+// WebKitTestRunnerApp
+//
+// Created by David Farler on 1/22/14.
+//
+//
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
diff --git a/Tools/WebKitTestRunner/WebKitTestRunnerPrefix.h b/Tools/WebKitTestRunner/WebKitTestRunnerPrefix.h
new file mode 100644
index 000000000..5f989fc54
--- /dev/null
+++ b/Tools/WebKitTestRunner/WebKitTestRunnerPrefix.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+
+#include <WebKit/WebKit2_C.h>
+#include <wtf/Platform.h>
+
+/* When C++ exceptions are disabled, the C++ library defines |try| and |catch|
+* to allow C++ code that expects exceptions to build. These definitions
+* interfere with Objective-C++ uses of Objective-C exception handlers, which
+* use |@try| and |@catch|. As a workaround, undefine these macros. */
+
+#ifdef __cplusplus
+#include <algorithm> // needed for exception_defines.h
+#endif
+
+#ifdef __OBJC__
+#undef try
+#undef catch
+#endif
+
diff --git a/Tools/WebKitTestRunner/WebNotificationProvider.cpp b/Tools/WebKitTestRunner/WebNotificationProvider.cpp
new file mode 100644
index 000000000..a46aabc2a
--- /dev/null
+++ b/Tools/WebKitTestRunner/WebNotificationProvider.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebNotificationProvider.h"
+
+#include <WebKit/WKMutableArray.h>
+#include <WebKit/WKNotification.h>
+#include <WebKit/WKNumber.h>
+#include <WebKit/WKSecurityOriginRef.h>
+#include <wtf/Assertions.h>
+
+namespace WTR {
+
+static void showWebNotification(WKPageRef page, WKNotificationRef notification, const void* clientInfo)
+{
+ static_cast<WebNotificationProvider*>(const_cast<void*>(clientInfo))->showWebNotification(page, notification);
+}
+
+static void closeWebNotification(WKNotificationRef notification, const void* clientInfo)
+{
+ static_cast<WebNotificationProvider*>(const_cast<void*>(clientInfo))->closeWebNotification(notification);
+}
+
+static void addNotificationManager(WKNotificationManagerRef manager, const void* clientInfo)
+{
+ static_cast<WebNotificationProvider*>(const_cast<void*>(clientInfo))->addNotificationManager(manager);
+}
+
+static void removeNotificationManager(WKNotificationManagerRef manager, const void* clientInfo)
+{
+ static_cast<WebNotificationProvider*>(const_cast<void*>(clientInfo))->removeNotificationManager(manager);
+}
+
+static WKDictionaryRef notificationPermissions(const void* clientInfo)
+{
+ return static_cast<WebNotificationProvider*>(const_cast<void*>(clientInfo))->notificationPermissions();
+}
+
+WebNotificationProvider::WebNotificationProvider()
+{
+}
+
+WebNotificationProvider::~WebNotificationProvider()
+{
+ if (m_notificationManager)
+ WKNotificationManagerSetProvider(m_notificationManager.get(), nullptr);
+}
+
+WKNotificationProviderV0 WebNotificationProvider::provider()
+{
+ WKNotificationProviderV0 notificationProvider = {
+ { 0, this },
+ WTR::showWebNotification,
+ WTR::closeWebNotification,
+ 0, // didDestroyNotification
+ WTR::addNotificationManager,
+ WTR::removeNotificationManager,
+ WTR::notificationPermissions,
+ 0, // clearNotifications
+ };
+ return notificationProvider;
+}
+
+void WebNotificationProvider::showWebNotification(WKPageRef, WKNotificationRef notification)
+{
+ if (!m_notificationManager)
+ return;
+
+ uint64_t id = WKNotificationGetID(notification);
+ ASSERT(!m_shownNotifications.contains(id));
+ m_shownNotifications.add(id);
+
+ WKNotificationManagerProviderDidShowNotification(m_notificationManager.get(), WKNotificationGetID(notification));
+}
+
+void WebNotificationProvider::closeWebNotification(WKNotificationRef notification)
+{
+ if (!m_notificationManager)
+ return;
+
+ uint64_t id = WKNotificationGetID(notification);
+ WKRetainPtr<WKUInt64Ref> wkID = WKUInt64Create(id);
+ WKRetainPtr<WKMutableArrayRef> array(AdoptWK, WKMutableArrayCreate());
+ WKArrayAppendItem(array.get(), wkID.get());
+ m_shownNotifications.remove(id);
+ WKNotificationManagerProviderDidCloseNotifications(m_notificationManager.get(), array.get());
+}
+
+void WebNotificationProvider::addNotificationManager(WKNotificationManagerRef manager)
+{
+ // We assume there is only one for testing.
+ ASSERT(!m_notificationManager);
+ m_notificationManager = manager;
+}
+
+void WebNotificationProvider::removeNotificationManager(WKNotificationManagerRef manager)
+{
+ // We assume there is only one for testing.
+ ASSERT(m_notificationManager);
+ ASSERT(m_notificationManager.get() == manager);
+ m_notificationManager = 0;
+}
+
+WKDictionaryRef WebNotificationProvider::notificationPermissions()
+{
+ // Initial permissions are always empty.
+ return WKMutableDictionaryCreate();
+}
+
+void WebNotificationProvider::simulateWebNotificationClick(uint64_t notificationID)
+{
+ if (!m_notificationManager)
+ return;
+
+ ASSERT(m_shownNotifications.contains(notificationID));
+ WKNotificationManagerProviderDidClickNotification(m_notificationManager.get(), notificationID);
+}
+
+void WebNotificationProvider::reset()
+{
+ if (!m_notificationManager) {
+ m_shownNotifications.clear();
+ return;
+ }
+
+ WKRetainPtr<WKMutableArrayRef> array(AdoptWK, WKMutableArrayCreate());
+ HashSet<uint64_t>::const_iterator itEnd = m_shownNotifications.end();
+ for (HashSet<uint64_t>::const_iterator it = m_shownNotifications.begin(); it != itEnd; ++it) {
+ WKRetainPtr<WKUInt64Ref> wkID = WKUInt64Create(*it);
+ WKArrayAppendItem(array.get(), wkID.get());
+ }
+
+ m_shownNotifications.clear();
+ WKNotificationManagerProviderDidCloseNotifications(m_notificationManager.get(), array.get());
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/WebNotificationProvider.h b/Tools/WebKitTestRunner/WebNotificationProvider.h
new file mode 100644
index 000000000..402d110ef
--- /dev/null
+++ b/Tools/WebKitTestRunner/WebNotificationProvider.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebNotificationProvider_h
+#define WebNotificationProvider_h
+
+#include <WebKit/WKNotificationManager.h>
+#include <WebKit/WKNotificationProvider.h>
+#include <WebKit/WKRetainPtr.h>
+#include <wtf/HashSet.h>
+
+namespace WTR {
+
+class WebNotificationProvider {
+public:
+ WebNotificationProvider();
+ ~WebNotificationProvider();
+ WKNotificationProviderV0 provider();
+
+ void showWebNotification(WKPageRef, WKNotificationRef);
+ void closeWebNotification(WKNotificationRef);
+ void addNotificationManager(WKNotificationManagerRef);
+ void removeNotificationManager(WKNotificationManagerRef);
+ WKDictionaryRef notificationPermissions();
+
+ void simulateWebNotificationClick(uint64_t notificationID);
+ void reset();
+
+private:
+ WKRetainPtr<WKNotificationManagerRef> m_notificationManager;
+ HashSet<uint64_t> m_shownNotifications;
+};
+
+}
+
+#endif
diff --git a/Tools/WebKitTestRunner/WorkQueueManager.cpp b/Tools/WebKitTestRunner/WorkQueueManager.cpp
new file mode 100644
index 000000000..63f491b47
--- /dev/null
+++ b/Tools/WebKitTestRunner/WorkQueueManager.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WorkQueueManager.h"
+
+#include "PlatformWebView.h"
+#include "TestController.h"
+#include <WebKit/WKPage.h>
+#include <WebKit/WKPagePrivate.h>
+#include <WebKit/WKRetainPtr.h>
+#include <stdio.h>
+#include <wtf/text/CString.h>
+
+namespace WTR {
+
+static inline WKPageRef mainPage()
+{
+ return TestController::singleton().mainWebView()->page();
+}
+
+static inline bool goToItemAtIndex(int index)
+{
+ WKBackForwardListRef backForwardList = WKPageGetBackForwardList(mainPage());
+ ASSERT(backForwardList);
+
+ WKBackForwardListItemRef listItem = WKBackForwardListGetItemAtIndex(backForwardList, index);
+ if (!listItem)
+ return false;
+
+ WKPageGoToBackForwardListItem(mainPage(), listItem);
+
+ return true;
+}
+
+class WorkQueueItem {
+public:
+ enum Type {
+ Loading,
+ NonLoading
+ };
+
+ virtual ~WorkQueueItem() { }
+ virtual Type invoke() const = 0;
+};
+
+// Required by WKPageRunJavaScriptInMainFrame().
+static void runJavaScriptFunction(WKSerializedScriptValueRef, WKErrorRef, void*)
+{
+}
+
+template <WorkQueueItem::Type type>
+class ScriptItem : public WorkQueueItem {
+public:
+ explicit ScriptItem(const String& script)
+ : m_script(AdoptWK, WKStringCreateWithUTF8CString(script.utf8().data()))
+ {
+ }
+
+ WorkQueueItem::Type invoke() const
+ {
+ WKPageRunJavaScriptInMainFrame(mainPage(), m_script.get(), 0, runJavaScriptFunction);
+ return type;
+ }
+
+ WKRetainPtr<WKStringRef> m_script;
+};
+
+class NavigationItem : public WorkQueueItem {
+public:
+ explicit NavigationItem(int index) : m_index(index) { }
+
+ WorkQueueItem::Type invoke() const
+ {
+ return goToItemAtIndex(m_index) ? WorkQueueItem::Loading : WorkQueueItem::NonLoading;
+ }
+
+ unsigned m_index;
+};
+
+WorkQueueManager::WorkQueueManager()
+ : m_processing(false)
+{
+}
+
+WorkQueueManager::~WorkQueueManager()
+{
+}
+
+void WorkQueueManager::clearWorkQueue()
+{
+ m_processing = false;
+ m_workQueue.clear();
+}
+
+bool WorkQueueManager::processWorkQueue()
+{
+ m_processing = false;
+ while (!m_processing && !m_workQueue.isEmpty()) {
+ std::unique_ptr<WorkQueueItem> item(m_workQueue.takeFirst());
+ m_processing = (item->invoke() == WorkQueueItem::Loading);
+ }
+
+ return !m_processing;
+}
+
+void WorkQueueManager::queueLoad(const String& url, const String& target, bool shouldOpenExternalURLs)
+{
+ class LoadItem : public WorkQueueItem {
+ public:
+ LoadItem(const String& url, const String& target, bool shouldOpenExternalURLs)
+ : m_url(AdoptWK, WKURLCreateWithUTF8CString(url.utf8().data()))
+ , m_target(target)
+ , m_shouldOpenExternalURLs(shouldOpenExternalURLs)
+ {
+ }
+
+ WorkQueueItem::Type invoke() const
+ {
+ if (!m_target.isEmpty()) {
+ // FIXME: Use target. Some layout tests cannot pass as they rely on this functionality.
+ fprintf(stderr, "queueLoad for a specific target is not implemented.\n");
+ return WorkQueueItem::NonLoading;
+ }
+ WKPageLoadURLWithShouldOpenExternalURLsPolicy(mainPage(), m_url.get(), m_shouldOpenExternalURLs);
+ return WorkQueueItem::Loading;
+ }
+
+ WKRetainPtr<WKURLRef> m_url;
+ String m_target;
+ bool m_shouldOpenExternalURLs;
+ };
+
+ enqueue(new LoadItem(url, target, shouldOpenExternalURLs));
+}
+
+void WorkQueueManager::queueLoadHTMLString(const String& content, const String& baseURL, const String& unreachableURL)
+{
+ class LoadHTMLStringItem : public WorkQueueItem {
+ public:
+ LoadHTMLStringItem(const String& content, const String& baseURL, const String& unreachableURL)
+ : m_content(AdoptWK, WKStringCreateWithUTF8CString(content.utf8().data()))
+ , m_baseURL(AdoptWK, WKURLCreateWithUTF8CString(baseURL.utf8().data()))
+ , m_unreachableURL(AdoptWK, WKURLCreateWithUTF8CString(unreachableURL.utf8().data()))
+ {
+ }
+
+ WorkQueueItem::Type invoke() const
+ {
+ WKPageLoadAlternateHTMLString(mainPage(), m_content.get(), m_baseURL.get(), m_unreachableURL.get());
+ return WorkQueueItem::Loading;
+ }
+
+ WKRetainPtr<WKStringRef> m_content;
+ WKRetainPtr<WKURLRef> m_baseURL;
+ WKRetainPtr<WKURLRef> m_unreachableURL;
+ };
+
+ enqueue(new LoadHTMLStringItem(content, baseURL, unreachableURL));
+}
+
+void WorkQueueManager::queueBackNavigation(unsigned howFarBackward)
+{
+ enqueue(new NavigationItem(-howFarBackward));
+}
+
+void WorkQueueManager::queueForwardNavigation(unsigned howFarForward)
+{
+ enqueue(new NavigationItem(howFarForward));
+}
+
+void WorkQueueManager::queueReload()
+{
+ class ReloadItem : public WorkQueueItem {
+ public:
+ WorkQueueItem::Type invoke() const
+ {
+ WKPageReload(mainPage());
+ return WorkQueueItem::Loading;
+ }
+ };
+
+ enqueue(new ReloadItem());
+}
+
+void WorkQueueManager::queueLoadingScript(const String& script)
+{
+ enqueue(new ScriptItem<WorkQueueItem::Loading>(script));
+}
+
+void WorkQueueManager::queueNonLoadingScript(const String& script)
+{
+ enqueue(new ScriptItem<WorkQueueItem::NonLoading>(script));
+}
+
+void WorkQueueManager::enqueue(WorkQueueItem* item)
+{
+ ASSERT(item);
+ if (m_processing) {
+ fprintf(stderr, "Attempt to enqueue a work item while queue is being processed.\n");
+ delete item;
+ return;
+ }
+
+ m_workQueue.append(std::unique_ptr<WorkQueueItem>(item));
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/WorkQueueManager.h b/Tools/WebKitTestRunner/WorkQueueManager.h
new file mode 100644
index 000000000..273d51191
--- /dev/null
+++ b/Tools/WebKitTestRunner/WorkQueueManager.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WorkQueueManager_h
+#define WorkQueueManager_h
+
+#include <wtf/Deque.h>
+#include <wtf/text/WTFString.h>
+
+namespace WTR {
+
+class WorkQueueManager {
+ WTF_MAKE_NONCOPYABLE(WorkQueueManager);
+public:
+ WorkQueueManager();
+ ~WorkQueueManager();
+
+ bool isWorkQueueEmpty() const { return m_workQueue.isEmpty(); }
+ void clearWorkQueue();
+ bool processWorkQueue(); // Returns 'true' if queue is processed (no new loading is started), returns 'false' otherwise.
+
+ void queueLoad(const String& url, const String& target, bool shouldOpenExternalURLs);
+ void queueLoadHTMLString(const String& content, const String& baseURL, const String& unreachableURL);
+ void queueBackNavigation(unsigned howFarBackward);
+ void queueForwardNavigation(unsigned howFarForward);
+ void queueReload();
+ void queueLoadingScript(const String& script);
+ void queueNonLoadingScript(const String& script);
+
+private:
+ typedef Deque<std::unique_ptr<class WorkQueueItem>> WorkQueue;
+
+ void enqueue(WorkQueueItem*); // Adopts pointer.
+
+ WorkQueue m_workQueue;
+ bool m_processing;
+};
+
+} // namespace WTR
+
+#endif // WorkQueueManager_h
diff --git a/Tools/WebKitTestRunner/cairo/TestInvocationCairo.cpp b/Tools/WebKitTestRunner/cairo/TestInvocationCairo.cpp
new file mode 100644
index 000000000..4fafb180d
--- /dev/null
+++ b/Tools/WebKitTestRunner/cairo/TestInvocationCairo.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * (C) 2011 Brent Fulgham <bfulgham@webkit.org>. All rights reserved.
+ * (C) 2010, 2011 Igalia S.L
+ * (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestInvocation.h"
+
+#include "PixelDumpSupport.h"
+#include "PlatformWebView.h"
+#include "TestController.h"
+#include <WebKit/WKImageCairo.h>
+#include <cairo/cairo.h>
+#include <cstdio>
+#include <wtf/Assertions.h>
+#include <wtf/MD5.h>
+#include <wtf/StringExtras.h>
+
+namespace WTR {
+
+void computeMD5HashStringForCairoSurface(cairo_surface_t* surface, char hashString[33])
+{
+ ASSERT(cairo_image_surface_get_format(surface) == CAIRO_FORMAT_ARGB32); // ImageDiff assumes 32 bit RGBA, we must as well.
+
+ size_t pixelsHigh = cairo_image_surface_get_height(surface);
+ size_t pixelsWide = cairo_image_surface_get_width(surface);
+ size_t bytesPerRow = cairo_image_surface_get_stride(surface);
+
+ MD5 md5Context;
+ unsigned char* bitmapData = static_cast<unsigned char*>(cairo_image_surface_get_data(surface));
+ for (size_t row = 0; row < pixelsHigh; ++row) {
+ md5Context.addBytes(bitmapData, 4 * pixelsWide);
+ bitmapData += bytesPerRow;
+ }
+ MD5::Digest hash;
+ md5Context.checksum(hash);
+
+ snprintf(hashString, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7],
+ hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]);
+}
+
+static cairo_status_t writeFunction(void* closure, const unsigned char* data, unsigned length)
+{
+ Vector<unsigned char>* in = reinterpret_cast<Vector<unsigned char>*>(closure);
+ in->append(data, length);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void dumpBitmap(cairo_surface_t* surface, const char* checksum)
+{
+ Vector<unsigned char> pixelData;
+ cairo_surface_write_to_png_stream(surface, writeFunction, &pixelData);
+ const size_t dataLength = pixelData.size();
+ const unsigned char* data = pixelData.data();
+
+ printPNG(data, dataLength, checksum);
+}
+
+static void paintRepaintRectOverlay(cairo_surface_t* surface, WKArrayRef repaintRects)
+{
+ cairo_t* context = cairo_create(surface);
+
+ cairo_push_group(context);
+
+ // Paint the gray mask over the original image.
+ cairo_set_source_rgba(context, 0, 0, 0, 0.66);
+ cairo_paint(context);
+
+ // Paint transparent rectangles over the mask to show the repainted regions.
+ cairo_set_source_rgba(context, 0, 0, 0, 0);
+ cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
+ size_t count = WKArrayGetSize(repaintRects);
+ for (size_t i = 0; i < count; ++i) {
+ WKRect rect = WKRectGetValue(static_cast<WKRectRef>(WKArrayGetItemAtIndex(repaintRects, i)));
+ cairo_rectangle(context, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
+ cairo_fill(context);
+ }
+
+ cairo_pop_group_to_source(context);
+ cairo_paint(context);
+
+ cairo_destroy(context);
+}
+
+void TestInvocation::dumpPixelsAndCompareWithExpected(WKImageRef, WKArrayRef repaintRects)
+{
+ cairo_surface_t* surface = WKImageCreateCairoSurface(TestController::singleton().mainWebView()->windowSnapshotImage().get());
+
+ if (repaintRects)
+ paintRepaintRectOverlay(surface, repaintRects);
+
+ char actualHash[33];
+ computeMD5HashStringForCairoSurface(surface, actualHash);
+ if (!compareActualHashToExpectedAndDumpResults(actualHash))
+ dumpBitmap(surface, actualHash);
+
+ cairo_surface_destroy(surface);
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/config.h b/Tools/WebKitTestRunner/config.h
new file mode 100644
index 000000000..4fb2a4b5e
--- /dev/null
+++ b/Tools/WebKitTestRunner/config.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebKitTestRunner_config_h
+#define WebKitTestRunner_config_h
+
+#if defined (BUILDING_WITH_CMAKE)
+#include "cmakeconfig.h"
+#endif
+
+#include <WebKit/WebKit2_C.h>
+#include <wtf/Platform.h>
+#include <wtf/ExportMacros.h>
+#include <runtime/JSExportMacros.h>
+
+// This is needed because we include WebCore's headers.
+// FIXME: We should include <WebCore/PlatformExportMacros.h> instead.
+#define WEBCORE_EXPORT
+
+#endif
diff --git a/Tools/WebKitTestRunner/gtk/EventSenderProxyGtk.cpp b/Tools/WebKitTestRunner/gtk/EventSenderProxyGtk.cpp
new file mode 100644
index 000000000..3c3ae0ce8
--- /dev/null
+++ b/Tools/WebKitTestRunner/gtk/EventSenderProxyGtk.cpp
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Zan Dobersek <zandobersek@gmail.com>
+ * Copyright (C) 2009 Holger Hans Peter Freyther
+ * Copyright (C) 2010 Igalia S.L.
+ * Copyright (c) 2010 Motorola Mobility, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "EventSenderProxy.h"
+
+#include "NotImplemented.h"
+#include "PlatformWebView.h"
+#include "TestController.h"
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/WTFString.h>
+
+namespace WTR {
+
+// WebCore and layout tests assume this value
+static const float pixelsPerScrollTick = 40;
+
+// Key event location code defined in DOM Level 3.
+enum KeyLocationCode {
+ DOMKeyLocationStandard = 0x00,
+ DOMKeyLocationLeft = 0x01,
+ DOMKeyLocationRight = 0x02,
+ DOMKeyLocationNumpad = 0x03
+};
+
+
+struct WTREventQueueItem {
+ GdkEvent* event;
+ gulong delay;
+
+ WTREventQueueItem()
+ : event(0)
+ , delay(0)
+ {
+ }
+ WTREventQueueItem(GdkEvent* event, gulong delay)
+ : event(event)
+ , delay(delay)
+ {
+ }
+};
+
+EventSenderProxy::EventSenderProxy(TestController* testController)
+ : m_testController(testController)
+ , m_time(0)
+ , m_leftMouseButtonDown(false)
+ , m_clickCount(0)
+ , m_clickTime(0)
+ , m_clickButton(kWKEventMouseButtonNoButton)
+ , m_mouseButtonCurrentlyDown(0)
+{
+}
+
+EventSenderProxy::~EventSenderProxy()
+{
+}
+
+static guint getMouseButtonModifiers(int gdkButton)
+{
+ if (gdkButton == 1)
+ return GDK_BUTTON1_MASK;
+ if (gdkButton == 2)
+ return GDK_BUTTON2_MASK;
+ if (gdkButton == 3)
+ return GDK_BUTTON3_MASK;
+ return 0;
+}
+
+static unsigned eventSenderButtonToGDKButton(unsigned button)
+{
+ int mouseButton = 3;
+ if (button <= 2)
+ mouseButton = button + 1;
+ // fast/events/mouse-click-events expects the 4th button to be treated as the middle button.
+ else if (button == 3)
+ mouseButton = 2;
+
+ return mouseButton;
+}
+
+static guint webkitModifiersToGDKModifiers(WKEventModifiers wkModifiers)
+{
+ guint modifiers = 0;
+
+ if (wkModifiers & kWKEventModifiersControlKey)
+ modifiers |= GDK_CONTROL_MASK;
+ if (wkModifiers & kWKEventModifiersShiftKey)
+ modifiers |= GDK_SHIFT_MASK;
+ if (wkModifiers & kWKEventModifiersAltKey)
+ modifiers |= GDK_MOD1_MASK;
+ if (wkModifiers & kWKEventModifiersMetaKey)
+ modifiers |= GDK_META_MASK;
+
+ return modifiers;
+}
+
+GdkEvent* EventSenderProxy::createMouseButtonEvent(GdkEventType eventType, unsigned button, WKEventModifiers modifiers)
+{
+ GdkEvent* mouseEvent = gdk_event_new(eventType);
+
+ mouseEvent->button.button = eventSenderButtonToGDKButton(button);
+ mouseEvent->button.x = m_position.x;
+ mouseEvent->button.y = m_position.y;
+ mouseEvent->button.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
+ g_object_ref(mouseEvent->button.window);
+ gdk_event_set_device(mouseEvent, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(mouseEvent->button.window))));
+ mouseEvent->button.state = webkitModifiersToGDKModifiers(modifiers) | getMouseButtonModifiers(mouseEvent->button.button);
+ mouseEvent->button.time = GDK_CURRENT_TIME;
+ mouseEvent->button.axes = 0;
+
+ int xRoot, yRoot;
+ gdk_window_get_root_coords(mouseEvent->button.window, m_position.x, m_position.y, &xRoot, &yRoot);
+ mouseEvent->button.x_root = xRoot;
+ mouseEvent->button.y_root = yRoot;
+
+ return mouseEvent;
+}
+
+void EventSenderProxy::updateClickCountForButton(int button)
+{
+ if (m_time - m_clickTime < 1 && m_position == m_clickPosition && button == m_clickButton) {
+ ++m_clickCount;
+ m_clickTime = m_time;
+ return;
+ }
+
+ m_clickCount = 1;
+ m_clickTime = m_time;
+ m_clickPosition = m_position;
+ m_clickButton = button;
+}
+
+void EventSenderProxy::dispatchEvent(GdkEvent* event)
+{
+ ASSERT(m_testController->mainWebView());
+
+ // If we are sending an escape key to the WebView, this has the side-effect of dismissing
+ // any current popups anyway. Chances are that the test is doing this to dismiss the popup
+ // anyway. Not all tests properly dismiss popup menus, so we still need to do it manually
+ // if this isn't an escape key press.
+ if (event->type != GDK_KEY_PRESS || event->key.keyval != GDK_KEY_Escape)
+ m_testController->mainWebView()->dismissAllPopupMenus();
+
+ gtk_main_do_event(event);
+ gdk_event_free(event);
+}
+
+void EventSenderProxy::replaySavedEvents()
+{
+ while (!m_eventQueue.isEmpty()) {
+ WTREventQueueItem item = m_eventQueue.takeFirst();
+ if (item.delay)
+ g_usleep(item.delay * 1000);
+
+ dispatchEvent(item.event);
+ }
+}
+
+void EventSenderProxy::sendOrQueueEvent(GdkEvent* event)
+{
+ if (m_eventQueue.isEmpty() || !m_eventQueue.last().delay) {
+ dispatchEvent(event);
+ return;
+ }
+
+ m_eventQueue.last().event = event;
+ replaySavedEvents();
+}
+
+int getGDKKeySymForKeyRef(WKStringRef keyRef, unsigned location, guint* modifiers)
+{
+ if (location == DOMKeyLocationNumpad) {
+ if (WKStringIsEqualToUTF8CString(keyRef, "leftArrow"))
+ return GDK_KEY_KP_Left;
+ if (WKStringIsEqualToUTF8CString(keyRef, "rightArror"))
+ return GDK_KEY_KP_Right;
+ if (WKStringIsEqualToUTF8CString(keyRef, "upArrow"))
+ return GDK_KEY_KP_Up;
+ if (WKStringIsEqualToUTF8CString(keyRef, "downArrow"))
+ return GDK_KEY_KP_Down;
+ if (WKStringIsEqualToUTF8CString(keyRef, "pageUp"))
+ return GDK_KEY_KP_Page_Up;
+ if (WKStringIsEqualToUTF8CString(keyRef, "pageDown"))
+ return GDK_KEY_KP_Page_Down;
+ if (WKStringIsEqualToUTF8CString(keyRef, "home"))
+ return GDK_KEY_KP_Home;
+ if (WKStringIsEqualToUTF8CString(keyRef, "end"))
+ return GDK_KEY_KP_End;
+ if (WKStringIsEqualToUTF8CString(keyRef, "insert"))
+ return GDK_KEY_KP_Insert;
+ if (WKStringIsEqualToUTF8CString(keyRef, "delete"))
+ return GDK_KEY_KP_Delete;
+
+ return GDK_KEY_VoidSymbol;
+ }
+
+ if (WKStringIsEqualToUTF8CString(keyRef, "leftArrow"))
+ return GDK_KEY_Left;
+ if (WKStringIsEqualToUTF8CString(keyRef, "rightArrow"))
+ return GDK_KEY_Right;
+ if (WKStringIsEqualToUTF8CString(keyRef, "upArrow"))
+ return GDK_KEY_Up;
+ if (WKStringIsEqualToUTF8CString(keyRef, "downArrow"))
+ return GDK_KEY_Down;
+ if (WKStringIsEqualToUTF8CString(keyRef, "pageUp"))
+ return GDK_KEY_Page_Up;
+ if (WKStringIsEqualToUTF8CString(keyRef, "pageDown"))
+ return GDK_KEY_Page_Down;
+ if (WKStringIsEqualToUTF8CString(keyRef, "home"))
+ return GDK_KEY_Home;
+ if (WKStringIsEqualToUTF8CString(keyRef, "end"))
+ return GDK_KEY_End;
+ if (WKStringIsEqualToUTF8CString(keyRef, "insert"))
+ return GDK_KEY_Insert;
+ if (WKStringIsEqualToUTF8CString(keyRef, "delete"))
+ return GDK_KEY_Delete;
+ if (WKStringIsEqualToUTF8CString(keyRef, "printScreen"))
+ return GDK_KEY_Print;
+ if (WKStringIsEqualToUTF8CString(keyRef, "menu"))
+ return GDK_KEY_Menu;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F1"))
+ return GDK_KEY_F1;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F2"))
+ return GDK_KEY_F2;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F3"))
+ return GDK_KEY_F3;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F4"))
+ return GDK_KEY_F4;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F5"))
+ return GDK_KEY_F5;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F6"))
+ return GDK_KEY_F6;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F7"))
+ return GDK_KEY_F7;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F8"))
+ return GDK_KEY_F8;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F9"))
+ return GDK_KEY_F9;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F10"))
+ return GDK_KEY_F10;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F11"))
+ return GDK_KEY_F11;
+ if (WKStringIsEqualToUTF8CString(keyRef, "F12"))
+ return GDK_KEY_F12;
+
+ size_t bufferSize = WKStringGetMaximumUTF8CStringSize(keyRef);
+ auto buffer = std::make_unique<char[]>(bufferSize);
+ WKStringGetUTF8CString(keyRef, buffer.get(), bufferSize);
+ char charCode = buffer.get()[0];
+
+ if (charCode == '\n' || charCode == '\r')
+ return GDK_KEY_Return;
+ if (charCode == '\t')
+ return GDK_KEY_Tab;
+ if (charCode == '\x8')
+ return GDK_KEY_BackSpace;
+ if (charCode == 0x001B)
+ return GDK_KEY_Escape;
+
+ if (WTF::isASCIIUpper(charCode))
+ *modifiers |= GDK_SHIFT_MASK;
+
+ return gdk_unicode_to_keyval(static_cast<guint32>(buffer.get()[0]));
+}
+
+void EventSenderProxy::keyDown(WKStringRef keyRef, WKEventModifiers wkModifiers, unsigned location)
+{
+ guint modifiers = webkitModifiersToGDKModifiers(wkModifiers);
+ int gdkKeySym = getGDKKeySymForKeyRef(keyRef, location, &modifiers);
+
+ GdkEvent* pressEvent = gdk_event_new(GDK_KEY_PRESS);
+ pressEvent->key.keyval = gdkKeySym;
+ pressEvent->key.state = modifiers;
+ pressEvent->key.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformWindow()));
+ g_object_ref(pressEvent->key.window);
+ gdk_event_set_device(pressEvent, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(pressEvent->key.window))));
+
+ GUniqueOutPtr<GdkKeymapKey> keys;
+ gint nKeys;
+ if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeySym, &keys.outPtr(), &nKeys))
+ pressEvent->key.hardware_keycode = keys.get()[0].keycode;
+
+ GdkEvent* releaseEvent = gdk_event_copy(pressEvent);
+ dispatchEvent(pressEvent);
+ releaseEvent->key.type = GDK_KEY_RELEASE;
+ dispatchEvent(releaseEvent);
+}
+
+void EventSenderProxy::mouseDown(unsigned button, WKEventModifiers wkModifiers)
+{
+ // If the same mouse button is already in the down position don't
+ // send another event as it may confuse Xvfb.
+ unsigned gdkButton = eventSenderButtonToGDKButton(button);
+ if (m_mouseButtonCurrentlyDown == gdkButton)
+ return;
+
+ m_mouseButtonCurrentlyDown = gdkButton;
+
+ // Normally GDK will send both GDK_BUTTON_PRESS and GDK_2BUTTON_PRESS for
+ // the second button press during double-clicks. WebKit GTK+ selectively
+ // ignores the first GDK_BUTTON_PRESS of that pair using gdk_event_peek.
+ // Since our events aren't ever going onto the GDK event queue, WebKit won't
+ // be able to filter out the first GDK_BUTTON_PRESS, so we just don't send
+ // it here. Eventually this code should probably figure out a way to get all
+ // appropriate events onto the event queue and this work-around should be
+ // removed.
+ updateClickCountForButton(button);
+
+ GdkEventType eventType;
+ if (m_clickCount == 2)
+ eventType = GDK_2BUTTON_PRESS;
+ else if (m_clickCount == 3)
+ eventType = GDK_3BUTTON_PRESS;
+ else
+ eventType = GDK_BUTTON_PRESS;
+
+ GdkEvent* event = createMouseButtonEvent(eventType, button, wkModifiers);
+ sendOrQueueEvent(event);
+}
+
+void EventSenderProxy::mouseUp(unsigned button, WKEventModifiers wkModifiers)
+{
+ m_clickButton = kWKEventMouseButtonNoButton;
+ GdkEvent* event = createMouseButtonEvent(GDK_BUTTON_RELEASE, button, wkModifiers);
+ sendOrQueueEvent(event);
+
+ if (m_mouseButtonCurrentlyDown == event->button.button)
+ m_mouseButtonCurrentlyDown = 0;
+ m_clickPosition = m_position;
+ m_clickTime = GDK_CURRENT_TIME;
+}
+
+void EventSenderProxy::mouseMoveTo(double x, double y)
+{
+ m_position.x = x;
+ m_position.y = y;
+
+ GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY);
+ event->motion.x = m_position.x;
+ event->motion.y = m_position.y;
+
+ event->motion.time = GDK_CURRENT_TIME;
+ event->motion.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
+ g_object_ref(event->motion.window);
+ gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(event->motion.window))));
+ event->motion.state = 0 | getMouseButtonModifiers(m_mouseButtonCurrentlyDown);
+ event->motion.axes = 0;
+
+ int xRoot, yRoot;
+ gdk_window_get_root_coords(gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView())), m_position.x, m_position.y , &xRoot, &yRoot);
+ event->motion.x_root = xRoot;
+ event->motion.y_root = yRoot;
+
+ sendOrQueueEvent(event);
+}
+
+void EventSenderProxy::mouseScrollBy(int horizontal, int vertical)
+{
+ // Copy behaviour of Qt and EFL - just return in case of (0,0) mouse scroll
+ if (!horizontal && !vertical)
+ return;
+
+ GdkEvent* event = gdk_event_new(GDK_SCROLL);
+ event->scroll.x = m_position.x;
+ event->scroll.y = m_position.y;
+ event->scroll.time = GDK_CURRENT_TIME;
+ event->scroll.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
+ g_object_ref(event->scroll.window);
+ gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(event->scroll.window))));
+
+ // For more than one tick in a scroll, we need smooth scroll event
+ if ((horizontal && vertical) || horizontal > 1 || horizontal < -1 || vertical > 1 || vertical < -1) {
+ event->scroll.direction = GDK_SCROLL_SMOOTH;
+ event->scroll.delta_x = -horizontal;
+ event->scroll.delta_y = -vertical;
+
+ sendOrQueueEvent(event);
+ return;
+ }
+
+ if (horizontal < 0)
+ event->scroll.direction = GDK_SCROLL_RIGHT;
+ else if (horizontal > 0)
+ event->scroll.direction = GDK_SCROLL_LEFT;
+ else if (vertical < 0)
+ event->scroll.direction = GDK_SCROLL_DOWN;
+ else if (vertical > 0)
+ event->scroll.direction = GDK_SCROLL_UP;
+ else
+ g_assert_not_reached();
+
+ sendOrQueueEvent(event);
+}
+
+void EventSenderProxy::continuousMouseScrollBy(int horizontal, int vertical, bool paged)
+{
+ // Gtk+ does not support paged scroll events.
+ g_return_if_fail(!paged);
+
+ GdkEvent* event = gdk_event_new(GDK_SCROLL);
+ event->scroll.x = m_position.x;
+ event->scroll.y = m_position.y;
+ event->scroll.time = GDK_CURRENT_TIME;
+ event->scroll.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
+ g_object_ref(event->scroll.window);
+ gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(event->scroll.window))));
+
+ event->scroll.direction = GDK_SCROLL_SMOOTH;
+ event->scroll.delta_x = -horizontal / pixelsPerScrollTick;
+ event->scroll.delta_y = -vertical / pixelsPerScrollTick;
+
+ sendOrQueueEvent(event);
+}
+
+void EventSenderProxy::mouseScrollByWithWheelAndMomentumPhases(int x, int y, int /*phase*/, int /*momentum*/)
+{
+ // Gtk+ does not have the concept of wheel gesture phases or momentum. Just relay to
+ // the mouse wheel handler.
+ mouseScrollBy(x, y);
+}
+
+void EventSenderProxy::leapForward(int milliseconds)
+{
+ if (m_eventQueue.isEmpty())
+ m_eventQueue.append(WTREventQueueItem());
+
+ m_eventQueue.last().delay = milliseconds;
+ m_time += milliseconds / 1000.0;
+}
+
+void updateEventCoordinates(GdkEvent* touchEvent, int x, int y)
+{
+ touchEvent->touch.x = x;
+ touchEvent->touch.y = y;
+
+ int xRoot, yRoot;
+ gdk_window_get_root_coords(touchEvent->touch.window, x, y, &xRoot, &yRoot);
+ touchEvent->touch.x_root = xRoot;
+ touchEvent->touch.y_root = yRoot;
+}
+
+GUniquePtr<GdkEvent> EventSenderProxy::createTouchEvent(GdkEventType eventType, int id)
+{
+ GUniquePtr<GdkEvent> touchEvent(gdk_event_new(eventType));
+
+ touchEvent->touch.sequence = static_cast<GdkEventSequence*>(GINT_TO_POINTER(id));
+ touchEvent->touch.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
+ g_object_ref(touchEvent->touch.window);
+ gdk_event_set_device(touchEvent.get(), gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(touchEvent->button.window))));
+ touchEvent->touch.time = GDK_CURRENT_TIME;
+
+ return touchEvent;
+}
+
+#if ENABLE(TOUCH_EVENTS)
+void EventSenderProxy::addTouchPoint(int x, int y)
+{
+ // Touch ID is array index plus one, so 0 is skipped.
+ GUniquePtr<GdkEvent> event = createTouchEvent(static_cast<GdkEventType>(GDK_TOUCH_BEGIN), m_touchEvents.size() + 1);
+ updateEventCoordinates(event.get(), x, y);
+ m_updatedTouchEvents.add(GPOINTER_TO_INT(event->touch.sequence));
+ m_touchEvents.append(std::move(event));
+}
+
+void EventSenderProxy::updateTouchPoint(int index, int x, int y)
+{
+ ASSERT(index >= 0 && index < m_touchEvents.size());
+
+ const auto& event = m_touchEvents[index];
+ ASSERT(event);
+
+ event->type = GDK_TOUCH_UPDATE;
+ updateEventCoordinates(event.get(), x, y);
+ m_updatedTouchEvents.add(GPOINTER_TO_INT(event->touch.sequence));
+}
+
+void EventSenderProxy::sendUpdatedTouchEvents()
+{
+ for (auto id : m_updatedTouchEvents)
+ sendOrQueueEvent(gdk_event_copy(m_touchEvents[id - 1].get()));
+
+ m_updatedTouchEvents.clear();
+}
+
+void EventSenderProxy::touchStart()
+{
+ sendUpdatedTouchEvents();
+}
+
+void EventSenderProxy::touchMove()
+{
+ sendUpdatedTouchEvents();
+}
+
+void EventSenderProxy::touchEnd()
+{
+ sendUpdatedTouchEvents();
+}
+
+void EventSenderProxy::touchCancel()
+{
+ notImplemented();
+}
+
+void EventSenderProxy::clearTouchPoints()
+{
+ m_updatedTouchEvents.clear();
+ m_touchEvents.clear();
+}
+
+void EventSenderProxy::releaseTouchPoint(int index)
+{
+ ASSERT(index >= 0 && index < m_touchEvents.size());
+
+ const auto& event = m_touchEvents[index];
+ event->type = GDK_TOUCH_END;
+ m_updatedTouchEvents.add(GPOINTER_TO_INT(event->touch.sequence));
+}
+
+void EventSenderProxy::cancelTouchPoint(int index)
+{
+ notImplemented();
+}
+
+void EventSenderProxy::setTouchPointRadius(int radiusX, int radiusY)
+{
+ notImplemented();
+}
+
+void EventSenderProxy::setTouchModifier(WKEventModifiers modifier, bool enable)
+{
+ guint state = webkitModifiersToGDKModifiers(modifier);
+
+ for (const auto& event : m_touchEvents) {
+ if (event->type == GDK_TOUCH_END)
+ continue;
+
+ if (enable)
+ event->touch.state |= state;
+ else
+ event->touch.state &= ~(state);
+
+ m_updatedTouchEvents.add(GPOINTER_TO_INT(event->touch.sequence));
+ }
+}
+#endif // ENABLE(TOUCH_EVENTS)
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/gtk/PlatformWebViewGtk.cpp b/Tools/WebKitTestRunner/gtk/PlatformWebViewGtk.cpp
new file mode 100644
index 000000000..ab9b16284
--- /dev/null
+++ b/Tools/WebKitTestRunner/gtk/PlatformWebViewGtk.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 University of Szeged. All rights reserved.
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PlatformWebView.h"
+
+#include <WebKit/WKImageCairo.h>
+#include <WebKit/WKViewPrivate.h>
+#include <gtk/gtk.h>
+#include <wtf/Assertions.h>
+
+namespace WTR {
+
+PlatformWebView::PlatformWebView(WKContextRef context, WKPageGroupRef pageGroup, WKPageRef relatedPage, const ViewOptions& options)
+ : m_view(WKViewCreate(context, pageGroup, relatedPage))
+ , m_window(gtk_window_new(GTK_WINDOW_POPUP))
+ , m_windowIsKey(true)
+ , m_options(options)
+{
+ gtk_container_add(GTK_CONTAINER(m_window), GTK_WIDGET(m_view));
+
+ GtkAllocation size = { 0, 0, 800, 600 };
+ gtk_widget_size_allocate(GTK_WIDGET(m_view), &size);
+ gtk_window_resize(GTK_WINDOW(m_window), 800, 600);
+ gtk_widget_show_all(m_window);
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+PlatformWebView::~PlatformWebView()
+{
+ gtk_widget_destroy(m_window);
+}
+
+void PlatformWebView::resizeTo(unsigned width, unsigned height)
+{
+ WKRect frame = windowFrame();
+ frame.size.width = width;
+ frame.size.height = height;
+ setWindowFrame(frame);
+}
+
+WKPageRef PlatformWebView::page()
+{
+ return WKViewGetPage(m_view);
+}
+
+void PlatformWebView::focus()
+{
+ WKViewSetFocus(m_view, true);
+ setWindowIsKey(true);
+}
+
+WKRect PlatformWebView::windowFrame()
+{
+ GtkAllocation geometry;
+ gdk_window_get_geometry(gtk_widget_get_window(GTK_WIDGET(m_window)),
+ &geometry.x, &geometry.y, &geometry.width, &geometry.height);
+
+ WKRect frame;
+ frame.origin.x = geometry.x;
+ frame.origin.y = geometry.y;
+ frame.size.width = geometry.width;
+ frame.size.height = geometry.height;
+ return frame;
+}
+
+void PlatformWebView::setWindowFrame(WKRect frame)
+{
+ gdk_window_move_resize(gtk_widget_get_window(GTK_WIDGET(m_window)),
+ frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
+ GtkAllocation size = { 0, 0, static_cast<int>(frame.size.width), static_cast<int>(frame.size.height) };
+ gtk_widget_size_allocate(GTK_WIDGET(m_view), &size);
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+void PlatformWebView::addChromeInputField()
+{
+}
+
+void PlatformWebView::removeChromeInputField()
+{
+}
+
+void PlatformWebView::makeWebViewFirstResponder()
+{
+}
+
+void PlatformWebView::changeWindowScaleIfNeeded(float)
+{
+}
+
+WKRetainPtr<WKImageRef> PlatformWebView::windowSnapshotImage()
+{
+ int width = gtk_widget_get_allocated_width(GTK_WIDGET(m_view));
+ int height = gtk_widget_get_allocated_height(GTK_WIDGET(m_view));
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ cairo_surface_t* imageSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+
+ cairo_t* context = cairo_create(imageSurface);
+ gtk_widget_draw(GTK_WIDGET(m_view), context);
+ cairo_destroy(context);
+
+ WKRetainPtr<WKImageRef> wkImage = adoptWK(WKImageCreateFromCairoSurface(imageSurface, 0 /* options */));
+
+ cairo_surface_destroy(imageSurface);
+ return wkImage;
+}
+
+void PlatformWebView::didInitializeClients()
+{
+}
+
+bool PlatformWebView::viewSupportsOptions(const ViewOptions&) const
+{
+ return true;
+}
+
+void PlatformWebView::dismissAllPopupMenus()
+{
+ // gtk_menu_popdown doesn't modify the GList of attached menus, so it should
+ // be safe to walk this list while calling it.
+ GList* attachedMenusList = gtk_menu_get_for_attach_widget(GTK_WIDGET(m_view));
+ g_list_foreach(attachedMenusList, [] (void* data, void*) {
+ ASSERT(data);
+ gtk_menu_popdown(GTK_MENU(data));
+ }, nullptr);
+}
+
+} // namespace WTR
+
diff --git a/Tools/WebKitTestRunner/gtk/TestControllerGtk.cpp b/Tools/WebKitTestRunner/gtk/TestControllerGtk.cpp
new file mode 100644
index 000000000..50fbfa90c
--- /dev/null
+++ b/Tools/WebKitTestRunner/gtk/TestControllerGtk.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestController.h"
+
+#include "PlatformWebView.h"
+#include <gtk/gtk.h>
+#include <wtf/Platform.h>
+#include <wtf/glib/GMainLoopSource.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/WTFString.h>
+
+namespace WTR {
+
+static GMainLoopSource timeoutSource;
+
+void TestController::notifyDone()
+{
+ gtk_main_quit();
+ timeoutSource.cancel();
+}
+
+void TestController::platformInitialize()
+{
+}
+
+void TestController::platformDestroy()
+{
+}
+
+void TestController::platformWillRunTest(const TestInvocation&)
+{
+}
+
+void TestController::platformRunUntil(bool&, double timeout)
+{
+ if (timeout > 0) {
+ timeoutSource.scheduleAfterDelay("[WTR] Test timeout source", [] {
+ fprintf(stderr, "FAIL: TestControllerRunLoop timed out.\n");
+ gtk_main_quit();
+ }, std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::duration<double>(timeout)));
+ } else
+ timeoutSource.cancel();
+ gtk_main();
+}
+
+static char* getEnvironmentVariableAsUTF8String(const char* variableName)
+{
+ const char* value = g_getenv(variableName);
+ if (!value) {
+ fprintf(stderr, "%s environment variable not found\n", variableName);
+ exit(0);
+ }
+ gsize bytesWritten;
+ return g_filename_to_utf8(value, -1, 0, &bytesWritten, 0);
+}
+
+void TestController::initializeInjectedBundlePath()
+{
+ GUniquePtr<char> utf8BundlePath(getEnvironmentVariableAsUTF8String("TEST_RUNNER_INJECTED_BUNDLE_FILENAME"));
+ m_injectedBundlePath.adopt(WKStringCreateWithUTF8CString(utf8BundlePath.get()));
+}
+
+void TestController::initializeTestPluginDirectory()
+{
+ GUniquePtr<char> testPluginPath(getEnvironmentVariableAsUTF8String("TEST_RUNNER_TEST_PLUGIN_PATH"));
+ m_testPluginDirectory.adopt(WKStringCreateWithUTF8CString(testPluginPath.get()));
+}
+
+void TestController::platformInitializeContext()
+{
+}
+
+void TestController::setHidden(bool hidden)
+{
+ if (!m_mainWebView)
+ return;
+ if (hidden)
+ gtk_widget_unmap(GTK_WIDGET(m_mainWebView->platformView()));
+ else
+ gtk_widget_map(GTK_WIDGET(m_mainWebView->platformView()));
+}
+
+void TestController::runModal(PlatformWebView*)
+{
+ // FIXME: Need to implement this to test showModalDialog.
+}
+
+const char* TestController::platformLibraryPathForTesting()
+{
+ return 0;
+}
+
+void TestController::platformConfigureViewForTest(const TestInvocation&)
+{
+}
+
+void TestController::platformResetPreferencesToConsistentValues()
+{
+ if (!m_mainWebView)
+ return;
+ m_mainWebView->dismissAllPopupMenus();
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/gtk/fonts/AHEM____.TTF b/Tools/WebKitTestRunner/gtk/fonts/AHEM____.TTF
new file mode 100644
index 000000000..ac81cb031
--- /dev/null
+++ b/Tools/WebKitTestRunner/gtk/fonts/AHEM____.TTF
Binary files differ
diff --git a/Tools/WebKitTestRunner/gtk/fonts/FontWithNoValidEncoding.fon b/Tools/WebKitTestRunner/gtk/fonts/FontWithNoValidEncoding.fon
new file mode 100644
index 000000000..8fff7d9c1
--- /dev/null
+++ b/Tools/WebKitTestRunner/gtk/fonts/FontWithNoValidEncoding.fon
Binary files differ
diff --git a/Tools/WebKitTestRunner/gtk/fonts/fonts.conf b/Tools/WebKitTestRunner/gtk/fonts/fonts.conf
new file mode 100644
index 000000000..2387e9581
--- /dev/null
+++ b/Tools/WebKitTestRunner/gtk/fonts/fonts.conf
@@ -0,0 +1,435 @@
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+<fontconfig>
+
+ <!-- Due to patent (http://freetype.sourceforge.net/patents.html)
+ issues hinting gives different results depending on the
+ freetype version of the linux distribution, avoiding hinting
+ gives more consistent results. When all the distributions
+ release freetype the 2.4, which enables by default the
+ hinting method that was patented, we could undo this change
+ and try the hinting again. -->
+ <match target="font">
+ <edit name="hinting" mode="assign">
+ <bool>false</bool>
+ </edit>
+ </match>
+
+ <!-- This system may have turned off selection of bitmap fonts, but
+ we must turn it on again, because we want to be able to test that
+ bitmap fonts with no valid encodings are *never* selected regardless
+ of the Fontconfig settings. So force Fontconfig to select our cruddy
+ bitmap font -->
+ <selectfont>
+ <acceptfont>
+ <pattern>
+ <patelt name="family">
+ <string>FontWithNoValidEncoding</string>
+ </patelt>
+ </pattern>
+ </acceptfont>
+ </selectfont>
+
+ <!-- The sans-serif font should be Liberation Serif -->
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>serif</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Serif</string>
+ </edit>
+ </match>
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>Times</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Serif</string>
+ </edit>
+ </match>
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>Times New Roman</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Serif</string>
+ </edit>
+ </match>
+
+ <!-- Until we find good fonts to use for cursive and fantasy
+ just use our serif font. -->
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>cursive</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Serif</string>
+ </edit>
+ </match>
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>fantasy</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Serif</string>
+ </edit>
+ </match>
+
+ <!-- The sans-serif font should be Liberation Sans -->
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>sans serif</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Sans</string>
+ </edit>
+ </match>
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>sans</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Sans</string>
+ </edit>
+ </match>
+ <!-- We need to ensure that layout tests that use "Helvetica" don't
+ fall back to the default serif font -->
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>Helvetica</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Sans</string>
+ </edit>
+ </match>
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>Arial</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Sans</string>
+ </edit>
+ </match>
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>Lucida Grande</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Sans</string>
+ </edit>
+ </match>
+
+ <!-- The Monospace font should be Liberation Mono -->
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>monospace</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Mono</string>
+ </edit>
+ </match>
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>mono</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Mono</string>
+ </edit>
+ </match>
+ <!-- We need to ensure that layout tests that use "Courier", "Courier New",
+ and "Monaco" (all monospace fonts) don't fall back to the default
+ serif font -->
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>Courier</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Mono</string>
+ </edit>
+ </match>
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>Courier New</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Mono</string>
+ </edit>
+ </match>
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>Monaco</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Mono</string>
+ </edit>
+ </match>
+
+ <!-- The following hinting specializations are adapted from those in the
+ Chromium test_shell. We try to duplicate their incredibly thorough
+ testing here -->
+ <match target="pattern">
+ <test name="family" compare="eq">
+ <string>NonAntiAliasedSans</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Sans</string>
+ </edit>
+ <edit name="antialias" mode="assign">
+ <bool>false</bool>
+ </edit>
+ </match>
+
+ <match target="pattern">
+ <test name="family" compare="eq">
+ <string>SlightHintedSerif</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Serif</string>
+ </edit>
+ <edit name="hinting" mode="assign">
+ <bool>true</bool>
+ </edit>
+ <edit name="hintstyle" mode="assign">
+ <const>hintslight</const>
+ </edit>
+ </match>
+
+ <match target="pattern">
+ <test name="family" compare="eq">
+ <string>NonHintedSans</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Sans</string>
+ </edit>
+ <!-- These deliberately contradict each other. The 'hinting' preference
+ should take priority -->
+ <edit name="hintstyle" mode="assign">
+ <const>hintfull</const>
+ </edit>
+ <edit name="hinting" mode="assign">
+ <bool>false</bool>
+ </edit>
+ </match>
+
+ <match target="pattern">
+ <test name="family" compare="eq">
+ <string>AutohintedSerif</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Serif</string>
+ </edit>
+ <edit name="hinting" mode="assign">
+ <bool>true</bool>
+ </edit>
+ <edit name="autohint" mode="assign">
+ <bool>true</bool>
+ </edit>
+ <edit name="hintstyle" mode="assign">
+ <const>hintmedium</const>
+ </edit>
+ </match>
+
+ <match target="pattern">
+ <test name="family" compare="eq">
+ <string>HintedSerif</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Serif</string>
+ </edit>
+ <edit name="hinting" mode="assign">
+ <bool>true</bool>
+ </edit>
+ <edit name="autohint" mode="assign">
+ <bool>false</bool>
+ </edit>
+ <edit name="hintstyle" mode="assign">
+ <const>hintmedium</const>
+ </edit>
+ </match>
+
+ <match target="pattern">
+ <test name="family" compare="eq">
+ <string>FullAndAutoHintedSerif</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Serif</string>
+ </edit>
+ <edit name="hinting" mode="assign">
+ <bool>true</bool>
+ </edit>
+ <edit name="autohint" mode="assign">
+ <bool>true</bool>
+ </edit>
+ <edit name="hintstyle" mode="assign">
+ <const>hintfull</const>
+ </edit>
+ </match>
+
+ <match target="pattern">
+ <test name="family" compare="eq">
+ <string>SubpixelEnabledSans</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Sans</string>
+ </edit>
+ <edit name="rgba" mode="assign">
+ <const>rgb</const>
+ </edit>
+ </match>
+
+ <match target="pattern">
+ <test name="family" compare="eq">
+ <string>SubpixelDisabledSans</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>Liberation Sans</string>
+ </edit>
+ <edit name="rgba" mode="assign">
+ <const>none</const>
+ </edit>
+ </match>
+
+ <!-- We need to enable simulated bold to for DejaVu Serif to ensure that we interpret
+ this property correctly in: platform/gtk/fonts/fontconfig-synthetic-bold.html -->
+ <match target="font">
+ <test qual="any" name="family">
+ <string>DejaVu Serif</string>
+ </test>
+ <test name="weight" compare="less_eq">
+ <const>medium</const>
+ </test>
+ <test target="pattern" name="weight" compare="more">
+ <const>medium</const>
+ </test>
+ <edit name="embolden" mode="assign">
+ <bool>true</bool>
+ </edit>
+ <edit name="weight" mode="assign">
+ <const>bold</const>
+ </edit>
+ </match>
+
+ <!-- We need to enable simulated oblique to for DejaVu Serif to ensure that we interpret
+ this property correctly in: platform/gtk/fonts/fontconfig-synthetic-oblique.html -->
+ <match target="font">
+ <test qual="any" name="family">
+ <string>DejaVu Serif</string>
+ </test>
+ <test name="slant">
+ <const>roman</const>
+ </test>
+ <test target="pattern" name="slant" compare="not_eq">
+ <const>roman</const>
+ </test>
+ <edit name="matrix" mode="assign">
+ <times>
+ <name>matrix</name>
+ <matrix><double>1</double><double>0.2</double>
+ <double>0</double><double>1</double>
+ </matrix>
+ </times>
+ </edit>
+ <edit name="slant" mode="assign">
+ <const>oblique</const>
+ </edit>
+ <edit name="embeddedbitmap" mode="assign">
+ <bool>false</bool>
+ </edit>
+ </match>
+
+ <!-- These fonts should be treated as identical by CSS font fallback. -->
+ <alias binding="same">
+ <family>FamilyStrongAliasedToFreeMono</family>
+ <accept>
+ <family>FreeMono</family>
+ </accept>
+ </alias>
+
+ <!-- These fonts should NOT be treated as identical by CSS font fallback. -->
+ <alias>
+ <family>FamilyWeakAliasedToFreeMono</family>
+ <accept>
+ <family>FreeMono</family>
+ </accept>
+ </alias>
+
+ <!-- If this font doesn't have a family name we are falling back. The fallback
+ font will certainly be one of the DejaVu fonts that we have in our
+ collection since they have a wide range of characters. Fontconfig might
+ choose DejaVu Sans or DejaVu Serif depending on the system, so we force
+ the use of DejaVu Sans in these situations to maintain consistency. -->
+ <match target="pattern">
+ <test qual="all" name="family" compare="eq">
+ <string></string>
+ </test>
+ <edit name="family" mode="append_last">
+ <string>DejaVu Sans</string>
+ </edit>
+ </match>
+
+ <config>
+ <!-- These are the default Unicode chars that are expected to be blank
+ in fonts. All other blank chars are assumed to be broken and won't
+ appear in the resulting charsets -->
+ <blank>
+ <int>0x0020</int> <!-- SPACE -->
+ <int>0x00A0</int> <!-- NO-BREAK SPACE -->
+ <int>0x00AD</int> <!-- SOFT HYPHEN -->
+ <int>0x034F</int> <!-- COMBINING GRAPHEME JOINER -->
+ <int>0x0600</int> <!-- ARABIC NUMBER SIGN -->
+ <int>0x0601</int> <!-- ARABIC SIGN SANAH -->
+ <int>0x0602</int> <!-- ARABIC FOOTNOTE MARKER -->
+ <int>0x0603</int> <!-- ARABIC SIGN SAFHA -->
+ <int>0x06DD</int> <!-- ARABIC END OF AYAH -->
+ <int>0x070F</int> <!-- SYRIAC ABBREVIATION MARK -->
+ <int>0x115F</int> <!-- HANGUL CHOSEONG FILLER -->
+ <int>0x1160</int> <!-- HANGUL JUNGSEONG FILLER -->
+ <int>0x1680</int> <!-- OGHAM SPACE MARK -->
+ <int>0x17B4</int> <!-- KHMER VOWEL INHERENT AQ -->
+ <int>0x17B5</int> <!-- KHMER VOWEL INHERENT AA -->
+ <int>0x180E</int> <!-- MONGOLIAN VOWEL SEPARATOR -->
+ <int>0x2000</int> <!-- EN QUAD -->
+ <int>0x2001</int> <!-- EM QUAD -->
+ <int>0x2002</int> <!-- EN SPACE -->
+ <int>0x2003</int> <!-- EM SPACE -->
+ <int>0x2004</int> <!-- THREE-PER-EM SPACE -->
+ <int>0x2005</int> <!-- FOUR-PER-EM SPACE -->
+ <int>0x2006</int> <!-- SIX-PER-EM SPACE -->
+ <int>0x2007</int> <!-- FIGURE SPACE -->
+ <int>0x2008</int> <!-- PUNCTUATION SPACE -->
+ <int>0x2009</int> <!-- THIN SPACE -->
+ <int>0x200A</int> <!-- HAIR SPACE -->
+ <int>0x200B</int> <!-- ZERO WIDTH SPACE -->
+ <int>0x200C</int> <!-- ZERO WIDTH NON-JOINER -->
+ <int>0x200D</int> <!-- ZERO WIDTH JOINER -->
+ <int>0x200E</int> <!-- LEFT-TO-RIGHT MARK -->
+ <int>0x200F</int> <!-- RIGHT-TO-LEFT MARK -->
+ <int>0x2028</int> <!-- LINE SEPARATOR -->
+ <int>0x2029</int> <!-- PARAGRAPH SEPARATOR -->
+ <int>0x202A</int> <!-- LEFT-TO-RIGHT EMBEDDING -->
+ <int>0x202B</int> <!-- RIGHT-TO-LEFT EMBEDDING -->
+ <int>0x202C</int> <!-- POP DIRECTIONAL FORMATTING -->
+ <int>0x202D</int> <!-- LEFT-TO-RIGHT override -->
+ <int>0x202E</int> <!-- RIGHT-TO-LEFT override -->
+ <int>0x202F</int> <!-- NARROW NO-BREAK SPACE -->
+ <int>0x205F</int> <!-- MEDIUM MATHEMATICAL SPACE -->
+ <int>0x2060</int> <!-- WORD JOINER -->
+ <int>0x2061</int> <!-- FUNCTION APPLICATION -->
+ <int>0x2062</int> <!-- INVISIBLE TIMES -->
+ <int>0x2063</int> <!-- INVISIBLE SEPARATOR -->
+ <int>0x206A</int> <!-- INHIBIT SYMMETRIC SWAPPING -->
+ <int>0x206B</int> <!-- ACTIVATE SYMMETRIC SWAPPING -->
+ <int>0x206C</int> <!-- INHIBIT ARABIC FORM SHAPING -->
+ <int>0x206D</int> <!-- ACTIVATE ARABIC FORM SHAPING -->
+ <int>0x206E</int> <!-- NATIONAL DIGIT SHAPES -->
+ <int>0x206F</int> <!-- NOMINAL DIGIT SHAPES -->
+ <int>0x3000</int> <!-- IDEOGRAPHIC SPACE -->
+ <int>0x3164</int> <!-- HANGUL FILLER -->
+ <int>0xFEFF</int> <!-- ZERO WIDTH NO-BREAK SPACE -->
+ <int>0xFFA0</int> <!-- HALFWIDTH HANGUL FILLER -->
+ <int>0xFFF9</int> <!-- INTERLINEAR ANNOTATION ANCHOR -->
+ <int>0xFFFA</int> <!-- INTERLINEAR ANNOTATION SEPARATOR -->
+ <int>0xFFFB</int> <!-- INTERLINEAR ANNOTATION TERMINATOR -->
+ </blank>
+ </config>
+</fontconfig>
diff --git a/Tools/WebKitTestRunner/gtk/main.cpp b/Tools/WebKitTestRunner/gtk/main.cpp
new file mode 100644
index 000000000..7947ce55c
--- /dev/null
+++ b/Tools/WebKitTestRunner/gtk/main.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "TestController.h"
+#include <WebKit/WKTextCheckerGtk.h>
+#include <gtk/gtk.h>
+#include <wtf/glib/GRefPtr.h>
+
+int main(int argc, char** argv)
+{
+ gtk_init(&argc, &argv);
+
+ GRefPtr<GPtrArray> languages = adoptGRef(g_ptr_array_new());
+ g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("en_US")));
+ g_ptr_array_add(languages.get(), nullptr);
+ WKTextCheckerSetSpellCheckingLanguages(reinterpret_cast<const char* const*>(languages->pdata));
+
+ // Prefer the not installed web and plugin processes.
+ WTR::TestController controller(argc, const_cast<const char**>(argv));
+
+ return 0;
+}
diff --git a/Tools/gtk/check-for-webkitdom-api-breaks b/Tools/gtk/check-for-webkitdom-api-breaks
new file mode 100755
index 000000000..b2354f13b
--- /dev/null
+++ b/Tools/gtk/check-for-webkitdom-api-breaks
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+# Copyright (C) 2013, 2014 Igalia S.L.
+# Copyright (C) 2013 Gustavo Noronha Silva <gns@gnome.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import common
+import os
+import sys
+import webkitdom
+
+EXPECTED_API_PATH = common.top_level_path('Source', 'WebCore', 'bindings', 'gobject', 'webkitdom.symbols')
+
+def read_built_api():
+ apis = set()
+ for file_path in webkitdom.get_all_webkitdom_symbol_files():
+ with open(file_path) as file_handle:
+ apis.update(set(file_handle.readlines()))
+ return apis
+
+def read_expected_api():
+ with open(EXPECTED_API_PATH) as file_handle:
+ return set([symbol.strip('\n').split('@', 1)[0] + '\n' for symbol in file_handle.readlines()])
+
+def check_api(expected_api, built_api):
+ missing_api = expected_api.difference(built_api)
+ new_api = built_api.difference(expected_api)
+
+ if missing_api:
+ sys.stderr.write("Missing API (API break!) detected in GObject DOM bindings\n")
+ sys.stderr.write(" %s\n" % " ".join(missing_api))
+ sys.stderr.flush()
+
+ if new_api:
+ sys.stdout.write("New API detected in GObject DOM bindings\n")
+ sys.stdout.write(" %s\n" % " ".join(new_api))
+
+ if missing_api:
+ # This test can give false positives because the GObject
+ # DOM bindings API varies depending on the compilation options.
+ # So this shouldn't be made fatal until we figure out a way to handle it.
+ # See https://bugs.webkit.org/show_bug.cgi?id=121481
+ return 0
+
+ if new_api:
+ sys.stdout.write("API compatible changes found in GObject DOM bindings.\n")
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(check_api(read_expected_api(), read_built_api()))
diff --git a/Tools/gtk/common.py b/Tools/gtk/common.py
new file mode 100644
index 000000000..f53cc95aa
--- /dev/null
+++ b/Tools/gtk/common.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python
+# Copyright (C) 2011 Igalia S.L.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import errno
+import os
+import select
+import subprocess
+import sys
+
+top_level_dir = None
+build_dir = None
+library_build_dir = None
+binary_build_dir = None
+build_types = ('Release', 'Debug')
+
+
+def top_level_path(*args):
+ global top_level_dir
+ if not top_level_dir:
+ top_level_dir = os.path.join(os.path.dirname(__file__), '..', '..')
+ return os.path.join(*(top_level_dir,) + args)
+
+
+def set_build_types(new_build_types):
+ global build_types
+ build_types = new_build_types
+
+
+def library_build_path(*args):
+ global library_build_dir
+ if not library_build_dir:
+ library_build_dir = build_path('lib', *args)
+ return library_build_dir
+
+
+def binary_build_path(*args):
+ global binary_build_dir
+ if not binary_build_dir:
+ binary_build_dir = build_path('bin', *args)
+ return binary_build_dir
+
+
+def get_build_path(fatal=True):
+ global build_dir
+ if build_dir:
+ return build_dir
+
+ def is_valid_build_directory(path):
+ return os.path.exists(os.path.join(path, 'CMakeCache.txt')) or \
+ os.path.exists(os.path.join(path, 'bin/MiniBrowser'))
+
+ if len(sys.argv[1:]) > 1 and os.path.exists(sys.argv[-1]) and is_valid_build_directory(sys.argv[-1]):
+ return sys.argv[-1]
+
+ # Debian and Ubuntu build both flavours of the library (with gtk2
+ # and with gtk3); they use directories build-2.0 and build-3.0 for
+ # that, which is not handled by the above cases; we check that the
+ # directory where we are called from is a valid build directory,
+ # which should handle pretty much all other non-standard cases.
+ build_dir = os.getcwd()
+ if is_valid_build_directory(build_dir):
+ return build_dir
+
+ global build_types
+ for build_type in build_types:
+ build_dir = top_level_path('WebKitBuild', build_type)
+ if is_valid_build_directory(build_dir):
+ return build_dir
+
+ # distcheck builds in a directory named _build in the top-level path.
+ build_dir = top_level_path("_build")
+ if is_valid_build_directory(build_dir):
+ return build_dir
+
+ build_dir = top_level_path()
+ if is_valid_build_directory(build_dir):
+ return build_dir
+
+ build_dir = top_level_path("WebKitBuild")
+ if is_valid_build_directory(build_dir):
+ return build_dir
+
+ print('Could not determine build directory.')
+ if fatal:
+ sys.exit(1)
+
+
+def build_path(*args):
+ return os.path.join(*(get_build_path(),) + args)
+
+
+def pkg_config_file_variable(package, variable):
+ process = subprocess.Popen(['pkg-config', '--variable=%s' % variable, package],
+ stdout=subprocess.PIPE)
+ stdout = process.communicate()[0].decode("utf-8")
+ if process.returncode:
+ return None
+ return stdout.strip()
+
+
+def prefix_of_pkg_config_file(package):
+ return pkg_config_file_variable(package, 'prefix')
+
+
+def parse_output_lines(fd, parse_line_callback):
+ output = ''
+ read_set = [fd]
+ while read_set:
+ try:
+ rlist, wlist, xlist = select.select(read_set, [], [])
+ except select.error as e:
+ parse_line_callback("WARNING: error while waiting for fd %d to become readable\n" % fd)
+ parse_line_callback(" error code: %d, error message: %s\n" % (e[0], e[1]))
+ continue
+
+ if fd in rlist:
+ try:
+ chunk = os.read(fd, 1024)
+ except OSError as e:
+ if e.errno == errno.EIO:
+ # Child process finished.
+ chunk = ''
+ else:
+ raise e
+ if not chunk:
+ read_set.remove(fd)
+
+ output += chunk
+ while '\n' in output:
+ pos = output.find('\n')
+ parse_line_callback(output[:pos + 1])
+ output = output[pos + 1:]
+
+ if not chunk and output:
+ parse_line_callback(output)
+ output = ''
diff --git a/Tools/gtk/generate-gtkdoc b/Tools/gtk/generate-gtkdoc
new file mode 100755
index 000000000..7044d2ad8
--- /dev/null
+++ b/Tools/gtk/generate-gtkdoc
@@ -0,0 +1,211 @@
+#!/usr/bin/env python
+# Copyright (C) 2011 Igalia S.L.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+from __future__ import print_function
+from ConfigParser import SafeConfigParser
+
+import argparse
+import codecs
+import common
+import glob
+import gtkdoc
+import logging
+import os.path
+import sys
+import webkitdom
+
+sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
+sys.stderr = codecs.getwriter("utf-8")(sys.stderr)
+
+def configure_logging(verbose):
+ level = logging.DEBUG if verbose else logging.INFO
+ logger = logging.getLogger('gtkdoc')
+ logger.setLevel(level)
+ handler = logging.StreamHandler()
+ handler.setLevel(level)
+ logger.addHandler(handler)
+ if level == logging.DEBUG:
+ handler.setFormatter(logging.Formatter('[%(asctime)s] %(message)s'))
+ else:
+ handler.setFormatter(logging.Formatter('%(message)s'))
+
+def get_gtkdoc_module_paths(cross_reference_deps):
+ dependent_packages = {
+ 'glib-2.0' : ['glib', 'gobject', 'gio'],
+ 'libsoup-2.4' : ['libsoup-2.4'],
+ 'gdk-pixbuf-2.0': ['gdk-pixbuf'],
+ 'gtk+-3.0' : ['gtk3', 'gdk3']
+ }
+
+ paths = []
+ html_dir = os.path.join('share', 'gtk-doc', 'html')
+ for package, modules in dependent_packages.iteritems():
+ prefix = common.prefix_of_pkg_config_file(package)
+ if prefix is None:
+ continue
+ for module in modules:
+ paths.append(os.path.join(prefix, html_dir, module))
+
+ for local_dep in cross_reference_deps:
+ paths.append(common.build_path('Documentation', local_dep, 'html'))
+ return paths
+
+def print_missing_api(generator):
+ missing_api = generator.api_missing_documentation()
+ if not missing_api:
+ return
+ print("\nThe following API are missing documentation:")
+ for api in missing_api:
+ print("\t{0}".format(api))
+
+def files_to_ignore(source_dirs, headers_with_gtkdoc):
+ """
+ Find files to ignore during documentation generation. We assume that if an
+ implementation file exists for a header with gtkdoc (say webkitfoo.cpp for
+ webkitfoo.h) we shouldn't ignore that file. Currently this holds true for all
+ of the WebKit project.
+ """
+ implementation_files = list(headers_with_gtkdoc)
+ for header in headers_with_gtkdoc:
+ def add_file_if_exists(file):
+ if os.path.isfile(file):
+ implementation_files.append(os.path.abspath(file))
+ header_name_without_extension = os.path.splitext(header)[0]
+ add_file_if_exists(header_name_without_extension + ".cpp")
+ add_file_if_exists(header_name_without_extension + ".c")
+
+ def file_should_be_ignored(file):
+ if os.path.splitext(file)[1] not in ['.h', '.c', '.cpp', '.cc']:
+ return False # These files are ignored anyway.
+ if not os.path.isfile(file):
+ return True
+ return os.path.abspath(file) not in implementation_files
+
+ all_files = sum([[os.path.join(dir, file) for file in os.listdir(dir)] for dir in source_dirs], [])
+ return filter(file_should_be_ignored, all_files)
+
+def get_generator_for_config(config_file, virtual_root, cross_reference_deps = []):
+ if not os.path.isfile(config_file):
+ return None
+
+ config = SafeConfigParser()
+ config.read(config_file)
+ module_name = config.sections()[0]
+ pkgconfig_file = config.get(module_name, 'pkgconfig_file')
+
+ if not os.path.isfile(pkgconfig_file):
+ return None
+
+ source_dirs = config.get(module_name, 'source_dirs').replace(';', ' ').split()
+ headers = [os.path.abspath(f) for f in config.get(module_name, 'headers').replace(';', ' ').split()]
+ return gtkdoc.PkgConfigGTKDoc(pkgconfig_file, {
+ 'decorator': 'WEBKIT_API|WEBKIT_DEPRECATED|WEBKIT_DEPRECATED_FOR\(.+\)',
+ 'deprecation_guard': 'WEBKIT_DISABLE_DEPRECATED',
+ 'library_path': common.library_build_path(),
+ 'virtual_root': virtual_root,
+ 'module_name': module_name,
+ 'namespace': config.get(module_name, 'namespace'),
+ 'doc_dir': config.get(module_name, 'doc_dir'),
+ 'output_dir': common.build_path('Documentation', module_name),
+ 'main_sgml_file': config.get(module_name, 'main_sgml_file'),
+ 'source_dirs': source_dirs,
+ 'headers': headers,
+ 'cflags': " ".join(config.get(module_name, 'cflags').split()),
+ 'cross_reference_deps': get_gtkdoc_module_paths(cross_reference_deps),
+ 'ignored_files': files_to_ignore(source_dirs, headers),
+ })
+
+def generate_doc(generator, skip_html):
+ print("\nGenerating {0} documentation...".format(generator.module_name))
+ generator.generate(not skip_html)
+ if generator.saw_warnings:
+ print_missing_api(generator)
+ return generator.saw_warnings
+
+def rebase_doc(generator):
+ print("\nRebasing {0} documentation...".format(generator.module_name))
+ try:
+ generator.rebase_installed_docs()
+ except Exception:
+ print("Rebase did not happen, likely no documentation is present.")
+
+def generate_documentation(generator):
+ if not arguments.rebase:
+ return generate_doc(generator, arguments.skip_html)
+
+ rebase_doc(generator)
+ return False
+
+def prepare_environment_for_gtkdoc_generation():
+ # We need to add the JavaScriptCore build directory to the PKG_CONFIG_PATH
+ # so that pkgconfig can properly resolve the libjavascriptcore dependency.
+ pkg_config_path = os.environ.get("PKG_CONFIG_PATH")
+ os.environ['PKG_CONFIG_PATH'] = common.build_path('Source', 'JavaScriptCore')
+ if pkg_config_path:
+ os.environ['PKG_CONFIG_PATH'] += ':' + pkg_config_path
+
+ # Newer versions of glib have deprecated g_type_init, so we need to disable
+ # that warning when running gtkdoc-scanobj by overriding the CFLAGS we use
+ # to compile it.
+ cflags = os.environ.get('CFLAGS', '')
+ cflags += ' -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32'
+
+ # In non-x86 architectures, when a pointer is cast to (void*) and
+ # back, the compiler thinks that the alignment is random. Since
+ # gtkdoc build is broken at any warning message, it is better to
+ # silence these false positives.
+ cflags += ' -Wno-cast-align'
+ os.environ['CFLAGS'] = cflags
+
+ # Paths from the GNUmakefile generated configuration files are relative to the build directory.
+ os.chdir(common.build_path())
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description='Generate gtkdoc for WebKit.')
+ parser.add_argument('-v', '--verbose', action='store_true',
+ help='Whether or not to run in verbose mode.')
+ parser.add_argument('--rebase', action='store_true',
+ help='When specified, run the tool in rebase mode.')
+ parser.add_argument('--skip-html', action='store_true',
+ help='Whether or not to skip HTML generation, which can be slow.')
+ parser.add_argument('--virtual-root', type=str, default='',
+ help='A temporary installation directory which is used as the root ' + \
+ 'where the actual installation prefix lives; this is mostly ' + \
+ 'useful for packagers, and should be set to what is given to ' + \
+ 'make install as DESTDIR.')
+
+ arguments = parser.parse_args()
+ configure_logging(arguments.verbose)
+
+ prepare_environment_for_gtkdoc_generation()
+
+ webkitdom_generator = get_generator_for_config(common.build_path('gtkdoc-webkitdom.cfg'), arguments.virtual_root)
+ if not webkitdom_generator:
+ print("gtkdoc-webkitdom.cfg does not exist! Skipping that documentation")
+ sys.exit(1)
+ webkitdom.write_doc_files(webkitdom_generator.module_name)
+ saw_warnings = generate_documentation(webkitdom_generator)
+ if saw_warnings:
+ sys.exit(saw_warnings)
+
+ webkit2_generator = get_generator_for_config(common.build_path('gtkdoc-webkit2gtk.cfg'), arguments.virtual_root, [webkitdom_generator.module_name])
+ if not webkit2_generator:
+ print("gtkdoc-webkit2gtk.cfg does not exist! Skipping that documentation")
+ sys.exit(1)
+ saw_warnings = generate_documentation(webkit2_generator)
+
+ sys.exit(saw_warnings)
diff --git a/Tools/gtk/generate-inspector-gresource-manifest.py b/Tools/gtk/generate-inspector-gresource-manifest.py
new file mode 100755
index 000000000..03060cfa2
--- /dev/null
+++ b/Tools/gtk/generate-inspector-gresource-manifest.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# Copyright (C) 2013 Igalia S.L.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import argparse
+import glob
+import os
+import sys
+
+COMPRESSIBLE_EXTENSIONS = ['.html', '.js', '.css', '.svg']
+BASE_DIR = 'WebInspectorUI/'
+
+
+def get_filenames(args):
+ filenames = []
+
+ for pattern in args:
+ paths = glob.glob(pattern)
+ for filename in paths:
+ base_dir_index = filename.rfind(BASE_DIR)
+ if base_dir_index != -1:
+ name = filename[base_dir_index + len(BASE_DIR):]
+ # The result should use forward slashes, thus make sure any os-specific
+ # separator, added by the glob.glob() call, is properly replaced
+ if os.sep != '/':
+ name = name.replace(os.sep, '/')
+ filenames.append(name)
+ return filenames
+
+
+def is_compressible(filename):
+ return os.path.splitext(filename)[1] in COMPRESSIBLE_EXTENSIONS
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description='Generate a GResources file for the inspector.')
+ parser.add_argument('--output', nargs='?', type=argparse.FileType('w'), default=sys.stdout,
+ help='the output file')
+ parser.add_argument('filenames', metavar='FILES', nargs='+',
+ help='the list of files to include')
+
+ args = parser.parse_args(sys.argv[1:])
+
+ args.output.write(\
+ """<?xml version=1.0 encoding=UTF-8?>
+ <gresources>
+ <gresource prefix="/org/webkitgtk/inspector">
+""")
+
+ for filename in get_filenames(args.filenames):
+ line = ' <file'
+ if is_compressible(filename):
+ line += ' compressed="true"'
+ if 'Images/gtk/' in filename:
+ line += ' alias="%s"' % filename.replace('gtk/', '')
+ line += '>%s</file>\n' % filename
+
+ args.output.write(line)
+
+ args.output.write(\
+ """ </gresource>
+</gresources>
+""")
+
diff --git a/Tools/gtk/gtkdoc.py b/Tools/gtk/gtkdoc.py
new file mode 100644
index 000000000..4c8237b0e
--- /dev/null
+++ b/Tools/gtk/gtkdoc.py
@@ -0,0 +1,439 @@
+# Copyright (C) 2011 Igalia S.L.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import errno
+import logging
+import os
+import os.path
+import subprocess
+import sys
+
+
+class GTKDoc(object):
+
+ """Class that controls a gtkdoc run.
+
+ Each instance of this class represents one gtkdoc configuration
+ and set of documentation. The gtkdoc package is a series of tools
+ run consecutively which converts inline C/C++ documentation into
+ docbook files and then into HTML. This class is suitable for
+ generating documentation or simply verifying correctness.
+
+ Keyword arguments:
+ output_dir -- The path where gtkdoc output should be placed. Generation
+ may overwrite file in this directory. Required.
+ module_name -- The name of the documentation module. For libraries this
+ is typically the library name. Required if not library path
+ is given.
+ source_dirs -- A list of paths to directories of source code to be scanned.
+ Required if headers is not specified.
+ ignored_files -- A list of filenames to ignore in the source directory. It is
+ only necessary to provide the basenames of these files.
+ Typically it is important to provide an updated list of
+ ignored files to prevent warnings about undocumented symbols.
+ headers -- A list of paths to headers to be scanned. Required if source_dirs
+ is not specified.
+ namespace -- The library namespace.
+ decorator -- If a decorator is used to unhide certain symbols in header
+ files this parameter is required for successful scanning.
+ (default '')
+ deprecation_guard -- gtkdoc tries to ensure that symbols marked as deprecated
+ are encased in this C preprocessor define. This is required
+ to avoid gtkdoc warnings. (default '')
+ cflags -- This parameter specifies any preprocessor flags necessary for
+ building the scanner binary during gtkdoc-scanobj. Typically
+ this includes all absolute include paths necessary to resolve
+ all header dependencies. (default '')
+ ldflags -- This parameter specifies any linker flags necessary for
+ building the scanner binary during gtkdoc-scanobj. Typically
+ this includes "-lyourlibraryname". (default '')
+ library_path -- This parameter specifies the path to the directory where you
+ library resides used for building the scanner binary during
+ gtkdoc-scanobj. (default '')
+
+ doc_dir -- The path to other documentation files necessary to build
+ the documentation. This files in this directory as well as
+ the files in the 'html' subdirectory will be copied
+ recursively into the output directory. (default '')
+ main_sgml_file -- The path or name (if a doc_dir is given) of the SGML file
+ that is the considered the main page of your documentation.
+ (default: <module_name>-docs.sgml)
+ version -- The version number of the module. If this is provided,
+ a version.xml file containing the version will be created
+ in the output directory during documentation generation.
+
+ interactive -- Whether or not errors or warnings should prompt the user
+ to continue or not. When this value is false, generation
+ will continue despite warnings. (default False)
+
+ virtual_root -- A temporary installation directory which is used as the root
+ where the actual installation prefix lives; this is mostly
+ useful for packagers, and should be set to what is given to
+ make install as DESTDIR.
+ """
+
+ def __init__(self, args):
+
+ # Parameters specific to scanning.
+ self.module_name = ''
+ self.source_dirs = []
+ self.headers = []
+ self.ignored_files = []
+ self.namespace = ''
+ self.decorator = ''
+ self.deprecation_guard = ''
+
+ # Parameters specific to gtkdoc-scanobj.
+ self.cflags = ''
+ self.ldflags = ''
+ self.library_path = ''
+
+ # Parameters specific to generation.
+ self.output_dir = ''
+ self.doc_dir = ''
+ self.main_sgml_file = ''
+
+ # Parameters specific to gtkdoc-fixxref.
+ self.cross_reference_deps = []
+
+ self.interactive = False
+
+ self.logger = logging.getLogger('gtkdoc')
+
+ for key, value in iter(args.items()):
+ setattr(self, key, value)
+
+ if not getattr(self, 'output_dir'):
+ raise Exception('output_dir not specified.')
+ if not getattr(self, 'module_name'):
+ raise Exception('module_name not specified.')
+ if not getattr(self, 'source_dirs') and not getattr(self, 'headers'):
+ raise Exception('Neither source_dirs nor headers specified.' % key)
+
+ # Make all paths absolute in case we were passed relative paths, since
+ # we change the current working directory when executing subcommands.
+ self.output_dir = os.path.abspath(self.output_dir)
+ self.source_dirs = [os.path.abspath(x) for x in self.source_dirs]
+ self.headers = [os.path.abspath(x) for x in self.headers]
+ if self.library_path:
+ self.library_path = os.path.abspath(self.library_path)
+
+ if not self.main_sgml_file:
+ self.main_sgml_file = self.module_name + "-docs.sgml"
+
+ def generate(self, html=True):
+ self.saw_warnings = False
+
+ self._copy_doc_files_to_output_dir(html)
+ self._write_version_xml()
+ self._run_gtkdoc_scan()
+ self._run_gtkdoc_scangobj()
+ self._run_gtkdoc_mkdb()
+
+ if not html:
+ return
+
+ self._run_gtkdoc_mkhtml()
+ self._run_gtkdoc_fixxref()
+
+ def _delete_file_if_exists(self, path):
+ if not os.access(path, os.F_OK | os.R_OK):
+ return
+ self.logger.debug('deleting %s', path)
+ os.unlink(path)
+
+ def _create_directory_if_nonexistent(self, path):
+ try:
+ os.makedirs(path)
+ except OSError as error:
+ if error.errno != errno.EEXIST:
+ raise
+
+ def _raise_exception_if_file_inaccessible(self, path):
+ if not os.path.exists(path) or not os.access(path, os.R_OK):
+ raise Exception("Could not access file at: %s" % path)
+
+ def _output_has_warnings(self, outputs):
+ for output in outputs:
+ if output and output.find('warning'):
+ return True
+ return False
+
+ def _ask_yes_or_no_question(self, question):
+ if not self.interactive:
+ return True
+
+ question += ' [y/N] '
+ answer = None
+ while answer != 'y' and answer != 'n' and answer != '':
+ answer = raw_input(question).lower()
+ return answer == 'y'
+
+ def _run_command(self, args, env=None, cwd=None, print_output=True, ignore_warnings=False):
+ if print_output:
+ self.logger.info("Running %s", args[0])
+ self.logger.debug("Full command args: %s", str(args))
+
+ process = subprocess.Popen(args, env=env, cwd=cwd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = [b.decode("utf-8") for b in process.communicate()]
+
+ if print_output:
+ if stdout:
+ try:
+ sys.stdout.write(stdout.encode("utf-8"))
+ except UnicodeDecodeError:
+ sys.stdout.write(stdout)
+ if stderr:
+ try:
+ sys.stderr.write(stderr.encode("utf-8"))
+ except UnicodeDecodeError:
+ sys.stderr.write(stderr)
+
+ if process.returncode != 0:
+ raise Exception('%s produced a non-zero return code %i'
+ % (args[0], process.returncode))
+
+ if not ignore_warnings and ('warning' in stderr or 'warning' in stdout):
+ self.saw_warnings = True
+ if not self._ask_yes_or_no_question('%s produced warnings, '
+ 'try to continue?' % args[0]):
+ raise Exception('%s step failed' % args[0])
+
+ return stdout.strip()
+
+ def _copy_doc_files_to_output_dir(self, html=True):
+ if not self.doc_dir:
+ self.logger.info('Not copying any files from doc directory,'
+ ' because no doc directory given.')
+ return
+
+ def copy_file_replacing_existing(src, dest):
+ if os.path.isdir(src):
+ self.logger.debug('skipped directory %s', src)
+ return
+ if not os.access(src, os.F_OK | os.R_OK):
+ self.logger.debug('skipped unreadable %s', src)
+ return
+
+ self._delete_file_if_exists(dest)
+
+ self.logger.debug('created %s', dest)
+ try:
+ os.link(src, dest)
+ except OSError:
+ os.symlink(src, dest)
+
+ def copy_all_files_in_directory(src, dest):
+ for path in os.listdir(src):
+ copy_file_replacing_existing(os.path.join(src, path),
+ os.path.join(dest, path))
+
+ self.logger.info('Copying template files to output directory...')
+ self._create_directory_if_nonexistent(self.output_dir)
+ copy_all_files_in_directory(self.doc_dir, self.output_dir)
+
+ if not html:
+ return
+
+ self.logger.info('Copying HTML files to output directory...')
+ html_src_dir = os.path.join(self.doc_dir, 'html')
+ html_dest_dir = os.path.join(self.output_dir, 'html')
+ self._create_directory_if_nonexistent(html_dest_dir)
+
+ if os.path.exists(html_src_dir):
+ copy_all_files_in_directory(html_src_dir, html_dest_dir)
+
+ def _write_version_xml(self):
+ if not self.version:
+ self.logger.info('No version specified, so not writing version.xml')
+ return
+
+ version_xml_path = os.path.join(self.output_dir, 'version.xml')
+ src_version_xml_path = os.path.join(self.doc_dir, 'version.xml')
+
+ # Don't overwrite version.xml if it was in the doc directory.
+ if os.path.exists(version_xml_path) and \
+ os.path.exists(src_version_xml_path):
+ return
+
+ output_file = open(version_xml_path, 'w')
+ output_file.write(self.version)
+ output_file.close()
+
+ def _ignored_files_basenames(self):
+ return ' '.join([os.path.basename(x) for x in self.ignored_files])
+
+ def _run_gtkdoc_scan(self):
+ args = ['gtkdoc-scan',
+ '--module=%s' % self.module_name,
+ '--rebuild-types']
+
+ if not self.headers:
+ # Each source directory should be have its own "--source-dir=" prefix.
+ args.extend(['--source-dir=%s' % path for path in self.source_dirs])
+
+ if self.decorator:
+ args.append('--ignore-decorators=%s' % self.decorator)
+ if self.deprecation_guard:
+ args.append('--deprecated-guards=%s' % self.deprecation_guard)
+ if self.output_dir:
+ args.append('--output-dir=%s' % self.output_dir)
+
+ # We only need to pass the list of ignored files if the we are not using an explicit list of headers.
+ if not self.headers:
+ # gtkdoc-scan wants the basenames of ignored headers, so strip the
+ # dirname. Different from "--source-dir", the headers should be
+ # specified as one long string.
+ ignored_files_basenames = self._ignored_files_basenames()
+ if ignored_files_basenames:
+ args.append('--ignore-headers=%s' % ignored_files_basenames)
+
+ if self.headers:
+ args.extend(self.headers)
+
+ self._run_command(args)
+
+ def _run_gtkdoc_scangobj(self):
+ env = os.environ
+ ldflags = self.ldflags
+ if self.library_path:
+ additional_ldflags = ''
+ for arg in env.get('LDFLAGS', '').split(' '):
+ if arg.startswith('-L'):
+ additional_ldflags = '%s %s' % (additional_ldflags, arg)
+ ldflags = ' "-L%s" %s ' % (self.library_path, additional_ldflags) + ldflags
+ current_ld_library_path = env.get('LD_LIBRARY_PATH')
+ if current_ld_library_path:
+ env['RUN'] = 'LD_LIBRARY_PATH="%s:%s" ' % (self.library_path, current_ld_library_path)
+ else:
+ env['RUN'] = 'LD_LIBRARY_PATH="%s" ' % self.library_path
+
+ if ldflags:
+ env['LDFLAGS'] = '%s %s' % (ldflags, env.get('LDFLAGS', ''))
+ if self.cflags:
+ env['CFLAGS'] = '%s %s' % (self.cflags, env.get('CFLAGS', ''))
+
+ if 'CFLAGS' in env:
+ self.logger.debug('CFLAGS=%s', env['CFLAGS'])
+ if 'LDFLAGS' in env:
+ self.logger.debug('LDFLAGS %s', env['LDFLAGS'])
+ if 'RUN' in env:
+ self.logger.debug('RUN=%s', env['RUN'])
+ self._run_command(['gtkdoc-scangobj', '--module=%s' % self.module_name],
+ env=env, cwd=self.output_dir)
+
+ def _run_gtkdoc_mkdb(self):
+ sgml_file = os.path.join(self.output_dir, self.main_sgml_file)
+ self._raise_exception_if_file_inaccessible(sgml_file)
+
+ args = ['gtkdoc-mkdb',
+ '--module=%s' % self.module_name,
+ '--main-sgml-file=%s' % sgml_file,
+ '--source-suffixes=h,c,cpp,cc',
+ '--output-format=xml',
+ '--sgml-mode']
+
+ if self.namespace:
+ args.append('--name-space=%s' % self.namespace)
+
+ ignored_files_basenames = self._ignored_files_basenames()
+ if ignored_files_basenames:
+ args.append('--ignore-files=%s' % ignored_files_basenames)
+
+ # Each directory should be have its own "--source-dir=" prefix.
+ args.extend(['--source-dir=%s' % path for path in self.source_dirs])
+ self._run_command(args, cwd=self.output_dir)
+
+ def _run_gtkdoc_mkhtml(self):
+ html_dest_dir = os.path.join(self.output_dir, 'html')
+ if not os.path.isdir(html_dest_dir):
+ raise Exception("%s is not a directory, could not generate HTML"
+ % html_dest_dir)
+ elif not os.access(html_dest_dir, os.X_OK | os.R_OK | os.W_OK):
+ raise Exception("Could not access %s to generate HTML"
+ % html_dest_dir)
+
+ # gtkdoc-mkhtml expects the SGML path to be absolute.
+ sgml_file = os.path.join(os.path.abspath(self.output_dir),
+ self.main_sgml_file)
+ self._raise_exception_if_file_inaccessible(sgml_file)
+
+ self._run_command(['gtkdoc-mkhtml', self.module_name, sgml_file],
+ cwd=html_dest_dir)
+
+ def _run_gtkdoc_fixxref(self):
+ args = ['gtkdoc-fixxref',
+ '--module-dir=html',
+ '--html-dir=html']
+ args.extend(['--extra-dir=%s' % extra_dir for extra_dir in self.cross_reference_deps])
+ self._run_command(args, cwd=self.output_dir, ignore_warnings=True)
+
+ def rebase_installed_docs(self):
+ if not os.path.isdir(self.output_dir):
+ raise Exception("Tried to rebase documentation before generating it.")
+ html_dir = os.path.join(self.virtual_root + self.prefix, 'share', 'gtk-doc', 'html', self.module_name)
+ if not os.path.isdir(html_dir):
+ return
+ args = ['gtkdoc-rebase',
+ '--relative',
+ '--html-dir=%s' % html_dir]
+ args.extend(['--other-dir=%s' % extra_dir for extra_dir in self.cross_reference_deps])
+ if self.virtual_root:
+ args.extend(['--dest-dir=%s' % self.virtual_root])
+ self._run_command(args, cwd=self.output_dir)
+
+ def api_missing_documentation(self):
+ unused_doc_file = os.path.join(self.output_dir, self.module_name + "-unused.txt")
+ if not os.path.exists(unused_doc_file) or not os.access(unused_doc_file, os.R_OK):
+ return []
+ return open(unused_doc_file).read().splitlines()
+
+class PkgConfigGTKDoc(GTKDoc):
+
+ """Class reads a library's pkgconfig file to guess gtkdoc parameters.
+
+ Some gtkdoc parameters can be guessed by reading a library's pkgconfig
+ file, including the cflags, ldflags and version parameters. If you
+ provide these parameters as well, they will be appended to the ones
+ guessed via the pkgconfig file.
+
+ Keyword arguments:
+ pkg_config_path -- Path to the pkgconfig file for the library. Required.
+ """
+
+ def __init__(self, pkg_config_path, args):
+ super(PkgConfigGTKDoc, self).__init__(args)
+
+ pkg_config = os.environ.get('PKG_CONFIG', 'pkg-config')
+
+ if not os.path.exists(pkg_config_path):
+ raise Exception('Could not find pkg-config file at: %s'
+ % pkg_config_path)
+
+ self.cflags += " " + self._run_command([pkg_config,
+ pkg_config_path,
+ '--cflags'], print_output=False)
+ self.ldflags += " " + self._run_command([pkg_config,
+ pkg_config_path,
+ '--libs'], print_output=False)
+ self.version = self._run_command([pkg_config,
+ pkg_config_path,
+ '--modversion'], print_output=False)
+ self.prefix = self._run_command([pkg_config,
+ pkg_config_path,
+ '--variable=prefix'], print_output=False)
diff --git a/Tools/gtk/install-dependencies b/Tools/gtk/install-dependencies
new file mode 100755
index 000000000..2d0fe86e9
--- /dev/null
+++ b/Tools/gtk/install-dependencies
@@ -0,0 +1,401 @@
+#!/bin/bash
+
+# This script needs to be run with root rights.
+if [ $UID -ne 0 ]; then
+ sudo $0
+ exit 0
+fi
+
+function printNotSupportedMessageAndExit() {
+ echo
+ echo "Currently this script only works for distributions supporting apt-get and yum."
+ echo "Please add support for your distribution: http://webkit.org/b/110693"
+ echo
+ exit 1
+}
+
+function checkInstaller {
+ # apt-get - Debian based distributions
+ apt-get --version &> /dev/null
+ if [ $? -eq 0 ]; then
+ installDependenciesWithApt
+ exit 0
+ fi
+
+ # dnf - Fedora 22 and above
+ dnf --version &> /dev/null
+ if [ $? -eq 0 ]; then
+ installFedoraDependencies dnf
+ exit 0
+ fi
+
+ # yum - Fedora 21 and below
+ yum --version &> /dev/null
+ if [ $? -eq 0 ]; then
+ installFedoraDependencies yum
+ exit 0
+ fi
+
+ # pacman - Arch Linux
+ # pacman --version and pacman --help both return non-0
+ pacman -Ss &> /dev/null
+ if [ $? -eq 0 ]; then
+ installDependenciesWithPacman
+ exit 0
+ fi
+
+ printNotSupportedMessageAndExit
+}
+
+function installDependenciesWithApt {
+ # These are dependencies necessary for building WebKitGTK+.
+ apt-get install \
+ autoconf \
+ automake \
+ autopoint \
+ autotools-dev \
+ bison \
+ cmake \
+ flex \
+ gawk \
+ gnome-common \
+ gperf \
+ gtk-doc-tools \
+ intltool \
+ itstool \
+ libatk1.0-dev \
+ libenchant-dev \
+ libfaad-dev \
+ libgeoclue-dev \
+ libgirepository1.0-dev \
+ libgl1-mesa-dev \
+ libgl1-mesa-glx \
+ libgnutls28-dev \
+ libgtk2.0-dev \
+ libgtk-3-dev \
+ libgudev-1.0-dev \
+ libharfbuzz-dev \
+ libhyphen-dev \
+ libicu-dev \
+ libjpeg-dev \
+ libmpg123-dev \
+ libnotify-dev \
+ libopus-dev \
+ liborc-0.4-dev \
+ libpango1.0-dev \
+ libpng12-dev \
+ libpulse-dev \
+ librsvg2-dev \
+ libsecret-1-dev \
+ libsoup2.4-dev \
+ libsqlite3-dev \
+ libtheora-dev \
+ libtool \
+ libvorbis-dev \
+ libwebp-dev \
+ libxcomposite-dev \
+ libxslt1-dev \
+ libxt-dev \
+ libxtst-dev \
+ libwayland-dev \
+ ruby \
+ xfonts-utils
+
+ # These are dependencies necessary for running tests.
+ apt-get install \
+ apache2 \
+ curl \
+ dbus-x11 \
+ libapache2-mod-bw \
+ libapache2-mod-php5 \
+ libgpg-error-dev \
+ pulseaudio-utils \
+ python-gi \
+ ruby \
+ ruby-json \
+ ruby-highline \
+ xvfb
+
+ # These are dependencies necessary for building the jhbuild.
+ apt-get install \
+ git \
+ gobject-introspection \
+ icon-naming-utils \
+ libcroco3-dev \
+ libegl1-mesa-dev \
+ libepoxy-dev \
+ libgcrypt11-dev \
+ libgpg-error-dev \
+ libjson-glib-dev \
+ liborc-0.4-dev \
+ libp11-kit-dev \
+ libpciaccess-dev \
+ libssl-dev \
+ libtiff5-dev \
+ libv4l-dev \
+ libxcb-xfixes0-dev \
+ libxfont-dev \
+ libxkbfile-dev \
+ llvm \
+ llvm-dev \
+ python-dev \
+ ragel \
+ x11proto-bigreqs-dev \
+ x11proto-composite-dev \
+ x11proto-gl-dev \
+ x11proto-input-dev \
+ x11proto-randr-dev \
+ x11proto-resource-dev \
+ x11proto-scrnsaver-dev \
+ x11proto-video-dev \
+ x11proto-xcmisc-dev \
+ x11proto-xf86dri-dev \
+ xfonts-utils \
+ xtrans-dev \
+ xutils-dev
+
+ # These are dependencies necessary for using webkit-patch
+ apt-get install \
+ git-svn \
+ subversion
+
+ # ninja is a faster build system than GNU make, but it doesn't
+ # exist on Ubuntu 12.04
+ apt-get install ninja-build || true
+}
+
+function installDependenciesWithPacman {
+ # These are dependencies necessary for building WebKitGTK+.
+ packages=" \
+ autoconf \
+ automake \
+ bison \
+ cmake \
+ file \
+ findutils \
+ flex \
+ gawk \
+ gcc \
+ gettext \
+ gnome-common \
+ gperf \
+ grep \
+ groff \
+ gzip \
+ hyphen \
+ libtool \
+ m4 \
+ make \
+ patch \
+ pkg-config \
+ sed \
+ texinfo \
+ util-linux \
+ which \
+ gtk-doc \
+ intltool \
+ itstool \
+ atk \
+ enchant \
+ faad2 \
+ geoclue \
+ gobject-introspection \
+ mesa \
+ mesa-libgl \
+ gnutls \
+ gtk2 \
+ gtk3 \
+ libsystemd \
+ harfbuzz \
+ harfbuzz-icu \
+ icu \
+ libjpeg-turbo \
+ mpg123 \
+ opus \
+ pango \
+ libnotify \
+ libpng \
+ libpulse \
+ librsvg \
+ libsecret \
+ libsoup \
+ sqlite \
+ libtheora \
+ libtool \
+ libvorbis \
+ libwebp \
+ libxcomposite \
+ libxslt \
+ libxt \
+ libxtst \
+ ninja \
+ ruby \
+ xorg-font-utils \
+ orc \
+ wayland"
+
+ # These are dependencies necessary for running tests.
+ # Note: apache-mod_bw is available in the AUR, but the main repos
+ # could not find ruby-json
+ packages="$packages \
+ apache \
+ curl \
+ hunspell \
+ hunspell-en \
+ php-apache \
+ libgpg-error \
+ pulseaudio \
+ python-gobject \
+ ruby \
+ ruby-highline \
+ xorg-server-xvfb"
+
+ # These are dependencies necessary for building the jhbuild.
+ # Note: Could not find libegl-mesa
+ packages="$packages \
+ git \
+ gobject-introspection \
+ icon-naming-utils \
+ libcroco \
+ libepoxy \
+ libgcrypt \
+ libgpg-error \
+ p11-kit \
+ libpciaccess \
+ libtiff \
+ libxfixes \
+ libxfont \
+ libxkbfile \
+ llvm \
+ python2 \
+ python2-lxml \
+ ragel \
+ bigreqsproto \
+ compositeproto \
+ glproto \
+ inputproto \
+ randrproto \
+ resourceproto \
+ scrnsaverproto \
+ videoproto \
+ xcmiscproto \
+ xf86driproto \
+ xorg-font-utils \
+ xorg-util-macros \
+ xtrans \
+ xorg-utils"
+
+ # These are dependencies necessary for using webkit-patch
+ packages="$packages \
+ svn"
+ pacman -S --needed $packages
+
+ echo "You will also need to follow the instructions on the Arch Wiki to make"
+ echo "'python' call python2 in the webkit folder"
+ echo "https://wiki.archlinux.org/index.php/Python#Dealing_with_version_problem_in_build_scripts"
+}
+
+function installFedoraDependencies {
+ # These are dependencies necessary for building WebKitGTK+.
+ $1 install \
+ atk-devel \
+ autoconf \
+ automake \
+ bison \
+ cairo-devel \
+ cmake \
+ enchant-devel \
+ flex \
+ fontconfig-devel \
+ freetype-devel \
+ gcc-c++ \
+ geoclue-devel \
+ gettext-devel \
+ gobject-introspection-devel \
+ gperf \
+ gstreamer1-devel \
+ gstreamer1-plugins-base-devel \
+ gtk-doc \
+ gtk2-devel \
+ gtk3-devel \
+ harfbuzz-devel \
+ hyphen-devel \
+ json-glib-devel \
+ libXt-devel \
+ libXtst-devel \
+ libgudev1-devel \
+ libicu-devel \
+ libjpeg-turbo-devel \
+ libnotify-devel \
+ libpng-devel \
+ libsecret-devel \
+ libsoup-devel \
+ libv4l-devel \
+ libwebp-devel \
+ libwayland-client-devel \
+ libwayland-server-devel \
+ libxslt-devel \
+ mesa-libGL-devel \
+ ninja-build \
+ openssl-devel \
+ pcre-devel \
+ perl-Switch \
+ perl-version \
+ pulseaudio-libs-devel \
+ python-devel \
+ orc-devel \
+ ruby \
+ sqlite-devel
+
+ # These are dependencies necessary for running tests.
+ $1 install \
+ curl \
+ dbus-x11 \
+ hunspell-en \
+ httpd \
+ libgpg-error-devel \
+ mod_bw \
+ mod_ssl \
+ perl-CGI \
+ php \
+ pulseaudio-utils \
+ pygobject3-base \
+ ruby \
+ rubygem-json \
+ rubygem-highline \
+ xorg-x11-server-Xvfb
+
+ # These are dependencies necessary for building the jhbuild.
+ $1 install \
+ docbook-utils \
+ docbook-utils-pdf \
+ git \
+ gobject-introspection \
+ icon-naming-utils \
+ itstool \
+ libXfont-devel \
+ libcroco-devel \
+ libepoxy-devel \
+ libgcrypt-devel \
+ libgpg-error-devel \
+ libp11-devel \
+ libpciaccess-devel \
+ libtiff-devel \
+ libxkbfile-devel \
+ llvm \
+ llvm-devel \
+ mesa-libEGL-devel \
+ ragel \
+ xorg-x11-font-utils \
+ xorg-x11-proto-devel \
+ xorg-x11-util-macros \
+ xorg-x11-xtrans-devel
+
+ # These are dependencies necessary for using webkit-patch
+ $1 install \
+ git-svn \
+ subversion
+}
+
+checkInstaller
+
diff --git a/Tools/gtk/jhbuild-optional.modules b/Tools/gtk/jhbuild-optional.modules
new file mode 100644
index 000000000..7a69f3658
--- /dev/null
+++ b/Tools/gtk/jhbuild-optional.modules
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<!DOCTYPE moduleset SYSTEM "moduleset.dtd">
+<?xml-stylesheet type="text/xsl" href="moduleset.xsl"?>
+<moduleset>
+
+ <!-- This file contains a set of modules that are hard to get on
+ certain distributions. Feel free to add more here, within reason,
+ as long as they are not built by default. -->
+
+ <repository type="tarball" name="ftp.gnome.org"
+ href="http://ftp.gnome.org"/>
+ <repository type="tarball" name="llvm.org"
+ href="http://llvm.org"/>
+
+ <autotools id="libsecret" autogen-sh="configure">
+ <branch repo="ftp.gnome.org"
+ module="/pub/GNOME/sources/libsecret/0.11/libsecret-0.11.tar.xz" version="0.11"
+ hash="sha256:e5399dfb61376a7500d20cb22715152780aa3a2c8a64281ec6bc8f0ebeb8b689"/>
+ </autotools>
+
+ <!-- dconf is interesting in case you need to make sure that you can
+ access your global settings from inside the jhbuild environment
+ (e.g running MiniBrowser behind a proxy needs to read settings
+ from the org.gnome.system.proxy GSettings scheme -->
+ <autotools id="dconf" autogen-sh="configure">
+ <branch repo="ftp.gnome.org"
+ module="/pub/GNOME/sources/dconf/0.14/dconf-0.14.1.tar.xz" version="0.14"
+ hash="sha256:59c58c5eafa0ebd616287d078f4441d5342870a44634445d324e88fc0287f0b8"/>
+ <dependencies>
+ <dep package="glib"/>
+ <dep package="gtk+"/>
+ <dep package="libxml2"/>
+ <dep package="vala"/>
+ </dependencies>
+ </autotools>
+
+ <autotools id="vala" autogenargs="--enable-vapigen" supports-non-srcdir-builds="no">
+ <pkg-config>libvala-0.20.pc</pkg-config>
+ <branch repo="ftp.gnome.org"
+ module="/pub/GNOME/sources/vala/0.17/vala-0.17.7.tar.xz" version="0.17"
+ hash="sha256:dfdb9b3cb1c455462a57fd71a7e832c3b7b4131e85d2d0a2030111dae9fae24f"/>
+ <dependencies>
+ <dep package="glib"/>
+ </dependencies>
+ </autotools>
+
+ <autotools id="llvm"
+ autogenargs="--enable-optimized --disable-terminfo --disable-zlib --enable-targets=host --disable-backtraces --disable-crash-overrides --disable-expensive-checks --disable-debug-runtime --disable-assertions --enable-shared">
+ <branch repo="llvm.org"
+ module="/releases/3.5.0/llvm-3.5.0.src.tar.xz" version="3.5.0" checkoutdir="llvm-3.5.0"
+ hash="sha256:28e199f368ef0a4666708f31c7991ad3bcc3a578342b0306526dd35f07595c03">
+ <patch file="llvm-elf-allow-fde-references-outside-the-2gb-range.patch" strip="1"/>
+ <patch file="llvm-elf-allow-fde-references-outside-the-2gb-range-arm64.patch" strip="1"/>
+ <patch file="llvm-elf-add-stackmaps.patch" strip="1"/>
+ <patch file="llvm-elf-add-stackmaps-arm64.patch" strip="1"/>
+ </branch>
+ </autotools>
+
+</moduleset>
diff --git a/Tools/gtk/jhbuild.modules b/Tools/gtk/jhbuild.modules
new file mode 100644
index 000000000..5bf05dd82
--- /dev/null
+++ b/Tools/gtk/jhbuild.modules
@@ -0,0 +1,414 @@
+<?xml version="1.0"?>
+<!DOCTYPE moduleset SYSTEM "moduleset.dtd">
+<?xml-stylesheet type="text/xsl" href="moduleset.xsl"?>
+<moduleset>
+
+ <metamodule id="webkitgtk-testing-dependencies">
+ <dependencies>
+ <dep package="cairo"/>
+ <dep package="fonts"/>
+ <dep package="dicts"/>
+ <dep package="fontconfig"/>
+ <dep package="freetype6"/>
+ <dep package="harfbuzz"/>
+ <dep package="libxml2"/>
+ <dep package="gdk-pixbuf"/>
+ <dep package="gtk+"/>
+ <dep package="glib"/>
+ <dep package="glib-networking"/>
+ <dep package="gnome-icon-theme"/>
+ <dep package="gnome-icon-theme-symbolic"/>
+ <dep package="gnome-themes-standard"/>
+ <dep package="gtk-doc"/>
+ <dep package="libsoup"/>
+ <dep package="atk"/>
+ <dep package="at-spi2-core"/>
+ <dep package="at-spi2-atk"/>
+ <dep package="gstreamer"/>
+ <dep package="gst-plugins-base"/>
+ <dep package="gst-plugins-good"/>
+ <dep package="gst-plugins-bad"/>
+ <dep package="gst-libav"/>
+ <dep package="xserver"/>
+ <dep package="mesa"/>
+ <dep package="openwebrtc"/>
+ <dep package="libseccomp"/>
+ </dependencies>
+ </metamodule>
+
+ <include href="jhbuild-optional.modules"/>
+
+ <repository type="git" name="github.com"
+ href="git://github.com"/>
+ <repository type="tarball" name="sourceware.org-mirror"
+ href="http://mirrors.kernel.org/sources.redhat.com/"/>
+ <repository type="tarball" name="ftp.gnome.org"
+ href="http://ftp.gnome.org"/>
+ <repository type="git" name="git.gnome.org"
+ href="git://git.gnome.org/"/>
+ <repository type="tarball" name="cairographics.org"
+ href="http://cairographics.org"/>
+ <repository type="tarball" name="freedesktop.org"
+ href="http://www.freedesktop.org"/>
+ <repository type="tarball" name="xorg"
+ href="http://xorg.freedesktop.org"/>
+ <repository type="tarball" name="ftp.freedesktop.org"
+ href="ftp://ftp.freedesktop.org"/>
+ <repository type="tarball" name="xmlsoft.org"
+ href="ftp://xmlsoft.org"/>
+ <repository type="tarball" name="gstreamer"
+ href="http://gstreamer.freedesktop.org/src/"/>
+ <repository type="tarball" name="savannah.gnu.org"
+ href="http://download.savannah.gnu.org/releases/"/>
+ <repository type="git" name="freedesktop-git"
+ href="http://anongit.freedesktop.org/git"/>
+
+ <autotools id="cairo"
+ autogenargs="--enable-gl=yes --enable-egl=yes --enable-glx=yes ac_cv_func_rsvg_pixbuf_from_file=no"
+ makeargs="">
+ <dependencies>
+ <dep package="fontconfig"/>
+ <dep package="pixman"/>
+ <dep package="glib"/>
+ </dependencies>
+ <branch module="releases/cairo-1.14.2.tar.xz" version="1.14.2"
+ repo="cairographics.org"
+ hash="sha1:c8da68aa66ca0855b5d0ff552766d3e8679e1d24"/>
+ </autotools>
+
+ <autotools id="pixman" autogen-sh="configure"
+ autogenargs="--enable-gtk=no">
+ <branch module="releases/pixman-0.32.6.tar.gz" version="0.32.6"
+ repo="cairographics.org"
+ hash="sha256:3dfed13b8060eadabf0a4945c7045b7793cc7e3e910e748a8bb0f0dc3e794904"
+ md5sum="3a30859719a41bd0f5cccffbfefdd4c2">
+ </branch>
+ </autotools>
+
+ <autotools id="fonts"
+ skip-autogen="true">
+ <branch repo="github.com" module="mrobinson/webkitgtk-test-fonts.git" checkoutdir="webkitgtk-test-fonts" tag="0.0.5"/>
+ </autotools>
+
+ <autotools id="dicts"
+ skip-autogen="true">
+ <branch repo="github.com" module="mrobinson/webkitgtk-test-dicts.git" checkoutdir="webkitgtk-test-dicts" tag="0.0.1"/>
+ </autotools>
+
+ <autotools id="freetype6" autogen-sh="configure">
+ <branch module="freetype/freetype-2.4.11.tar.bz2" version="2.4.11"
+ repo="savannah.gnu.org"
+ hash="sha256:ef9d0bcb64647d9e5125dc7534d7ca371c98310fec87677c410f397f71ffbe3f"
+ md5sum="b93435488942486c8d0ca22e8f768034">
+ <patch file="freetype6-2.4.11-truetype-font-height-fix.patch" strip="1"/>
+ </branch>
+ </autotools>
+
+ <autotools id="harfbuzz" autogen-sh="configure">
+ <branch module="software/harfbuzz/release/harfbuzz-0.9.14.tar.bz2" version="0.9.14"
+ checkoutdir="harfbuzz-0.9.14"
+ repo="freedesktop.org"
+ hash="sha256:d07c0ffdbbbfdfbb6c65e73fe9c76466e87dbf04b094cbd0abf5fd7d571a4004"
+ md5sum="7e1990b79060e98e2d31f677a0ac9eed">
+ </branch>
+ </autotools>
+
+ <autotools id="libffi" autogen-sh="configure">
+ <branch module="libffi/libffi-3.1.tar.gz" version="3.1"
+ repo="sourceware.org-mirror"
+ hash="sha256:97feeeadca5e21870fa4433bc953d1b3af3f698d5df8a428f68b73cd60aef6eb"
+ md5sum="f5898b29bbfd70502831a212d9249d10"/>
+ </autotools>
+
+ <autotools id="libseccomp" autogen-sh="./autogen.sh; ./configure">
+ <branch repo="github.com" module="seccomp/libseccomp.git" tag="v2.2.3"/>
+ </autotools>
+
+ <autotools id="gdk-pixbuf" autogen-sh="configure"
+ autogenargs="--disable-introspection">
+ <dependencies>
+ <dep package="glib"/>
+ </dependencies>
+ <branch module="/pub/GNOME/sources/gdk-pixbuf/2.30/gdk-pixbuf-2.30.8.tar.xz" version="2.30.8"
+ repo="ftp.gnome.org"
+ hash="sha256:4853830616113db4435837992c0aebd94cbb993c44dc55063cee7f72a7bef8be"/>
+ </autotools>
+
+ <autotools id="librsvg" autogen-sh="configure"
+ autogenargs="--disable-introspection --enable-pixbuf-loader --disable-gtk-theme">
+ <dependencies>
+ <dep package="gdk-pixbuf"/>
+ <dep package="glib"/>
+ <dep package="cairo"/>
+ </dependencies>
+ <branch module="/pub/GNOME/sources/librsvg/2.36/librsvg-2.36.1.tar.xz" version="2.36.1"
+ repo="ftp.gnome.org"
+ hash="sha256:786b95e1a091375c5ef2997a21c69ff24d7077afeff18197355f54d9dcbcd8c5"
+ md5sum="89d483f30a7c77245b7ee02faaea5a5a">
+ <patch file="librsvg-2.36.1-bump-up-config.guess-to-support-aarch64.patch" strip="1"/>
+ </branch>
+ </autotools>
+
+ <autotools id="gtk+" autogen-sh="configure"
+ autogenargs="--disable-introspection">
+ <dependencies>
+ <dep package="glib"/>
+ <dep package="cairo"/>
+ <dep package="at-spi2-atk"/>
+ <dep package="gdk-pixbuf"/>
+ <dep package="pango"/>
+ </dependencies>
+ <branch module="/pub/GNOME/sources/gtk+/3.16/gtk+-3.16.4.tar.xz" version="3.16.4"
+ repo="ftp.gnome.org"
+ hash="sha256:1ee5dbd7a4cb81a91eaa1b7ae64ba5a3eab6a3c0a764155583ab96524590fc8e"/>
+ </autotools>
+
+ <autotools id="glib"
+ autogen-sh="configure"
+ autogenargs="--disable-dtrace">
+ <dependencies>
+ <dep package="libffi"/>
+ </dependencies>
+ <branch module="/pub/GNOME/sources/glib/2.44/glib-2.44.1.tar.xz" version="2.44.1"
+ repo="ftp.gnome.org"
+ hash="sha256:8811deacaf8a503d0a9b701777ea079ca6a4277be10e3d730d2112735d5eca07">
+ <patch file="glib-warning-fix.patch" strip="1"/>
+ </branch>
+ </autotools>
+
+ <autotools id="glib-networking">
+ <dependencies>
+ <dep package="glib"/>
+ </dependencies>
+ <branch module="/pub/GNOME/sources/glib-networking/2.41/glib-networking-2.41.4.tar.xz" version="2.41.4"
+ repo="ftp.gnome.org"
+ hash="sha256:930ad618865dcf81765d0f48cb6f13e22d76203efa59d30604aed0384ce80fd7"
+ md5sum="f88e163322c0834f9781d6224771ab2e"/>
+ </autotools>
+
+ <autotools id="libsoup"
+ autogenargs="--without-gnome --disable-introspection">
+ <dependencies>
+ <dep package="glib-networking"/>
+ </dependencies>
+ <branch module="libsoup" version="2.49.91.1"
+ repo="git.gnome.org"
+ tag="f7292448160128b3cb4b7cd06c2447f81fd8c6c9"/>
+ </autotools>
+
+ <autotools id="fontconfig"
+ autogen-sh="configure"
+ autogenargs="--enable-libxml2">
+ <dependencies>
+ <dep package="freetype6"/>
+ <dep package="libxml2"/>
+ </dependencies>
+ <branch module="software/fontconfig/release/fontconfig-2.11.1.tar.gz" version="2.11.1"
+ repo="freedesktop.org"
+ hash="sha256:b6b066c7dce3f436fdc0dfbae9d36122b38094f4f53bd8dffd45e195b0540d8d"
+ md5sum="e75e303b4f7756c2b16203a57ac87eba"/>
+ </autotools>
+
+ <autotools id="gnome-icon-theme" autogen-sh="configure">
+ <dependencies>
+ <dep package="gtk+"/>
+ </dependencies>
+ <branch module="pub/GNOME/sources/gnome-icon-theme/3.2/gnome-icon-theme-3.2.1.tar.xz" version="3.2.1"
+ repo="ftp.gnome.org"
+ hash="sha256:a7f0a8b17e91ac338fdbc01ac59a8738e9c1e201de492c070d43aacf291a8959"
+ md5sum="40be1e5a6eae11181311a6fc432cf892">
+ </branch>
+ </autotools>
+
+ <autotools id="gnome-icon-theme-symbolic" autogen-sh="configure">
+ <dependencies>
+ <dep package="gtk+"/>
+ </dependencies>
+ <branch module="pub/GNOME/sources/gnome-icon-theme-symbolic/3.2/gnome-icon-theme-symbolic-3.2.1.tar.xz" version="3.2.1"
+ repo="ftp.gnome.org"
+ hash="sha256:a558af2f87f761f00421f49c1addd2149b70228158e09327fa861219ac1a63cb"
+ md5sum="94137d3c256f2cc80298a9bef15d68c4">
+ </branch>
+ </autotools>
+
+ <autotools id="gnome-themes-standard" autogen-sh="configure">
+ <dependencies>
+ <dep package="gtk+"/>
+ <dep package="librsvg"/>
+ </dependencies>
+ <branch module="pub/GNOME/sources/gnome-themes-standard/3.6/gnome-themes-standard-3.6.0.tar.xz" version="3.6.0"
+ repo="ftp.gnome.org"
+ hash="sha256:d832fd38f7659f470df5ddc52131a59f989c75f3a70f8b3a514f89d90d4f43ec">
+ </branch>
+ </autotools>
+
+ <autotools id="atk"
+ autogen-sh="configure"
+ autogenargs="--disable-introspection">
+ <branch module="pub/GNOME/sources/atk/2.15/atk-2.15.4.tar.xz" version="2.15.4"
+ repo="ftp.gnome.org"
+ hash="sha256:0dddfa73a02178ca21a8de172c86d699aa887b4efeec736b4c8721eee4ac349c"/>
+ </autotools>
+
+ <autotools id="at-spi2-core"
+ autogenargs="--disable-introspection">
+ <branch module="pub/GNOME/sources/at-spi2-core/2.10/at-spi2-core-2.10.0.tar.xz" version="2.10.0"
+ repo="ftp.gnome.org"
+ hash="sha256:964155c7574220a00e11e1c0d91f2d3017ed603920eb1333ff9cbdb6a22744db">
+ </branch>
+ <dependencies>
+ <dep package="glib"/>
+ </dependencies>
+ </autotools>
+
+ <autotools id="at-spi2-atk">
+ <branch module="pub/GNOME/sources/at-spi2-atk/2.10/at-spi2-atk-2.10.0.tar.xz" version="2.10.0"
+ repo="ftp.gnome.org"
+ hash="sha256:dea7ff2f9bc9bbdb0351112616d738de718b55739cd2511afecac51604c31a94">
+ </branch>
+ <dependencies>
+ <dep package="glib"/>
+ <dep package="atk"/>
+ <dep package="at-spi2-core"/>
+ </dependencies>
+ </autotools>
+
+ <autotools id="libxml2"
+ autogen-sh="configure"
+ autogenargs="--without-python">
+ <branch module="/libxml2/libxml2-2.9.1.tar.gz" version="2.9.1"
+ repo="xmlsoft.org"
+ hash="sha256:fd3c64cb66f2c4ea27e934d275904d92cec494a8e8405613780cbc8a71680fdb"
+ md5sum="9c0cfef285d5c4a5c80d00904ddab380"/>
+ </autotools>
+
+ <autotools id="gstreamer" autogenargs="--disable-gtk-doc">
+ <branch module="gstreamer/gstreamer-1.4.4.tar.xz" version="1.4.4"
+ repo="gstreamer"
+ hash="sha256:f0e305d91a93d05bf9e332cd4256ca07d77f5186a4d73847b7ae6db218f2c237"
+ md5sum="98f4a6d45a28dd195144baef0244ba38"/>
+ </autotools>
+
+ <autotools id="gst-plugins-base"
+ autogen-sh="autogen.sh"
+ autogenargs="--disable-examples --disable-gtk-doc">
+ <dependencies>
+ <dep package="gstreamer"/>
+ </dependencies>
+ <branch module="gst-plugins-base/gst-plugins-base-1.4.4.tar.xz" version="1.4.4"
+ repo="gstreamer"
+ hash="sha256:49cd9e8f23c416b1607b43837a09833fa03e0106929d81ead2ddfde6c0ade44b"
+ md5sum="0c42eca8f9e4efd56d2ce8e9249ce4a1">
+ <patch file="gst-plugins-base-rtp-rtcpbuffer-fix-typo-in-enum.patch" strip="1"/>
+ </branch>
+ </autotools>
+
+ <autotools id="gst-plugins-good" autogenargs="--disable-examples --disable-soup --disable-gtk-doc">
+ <dependencies>
+ <dep package="gst-plugins-base"/>
+ </dependencies>
+
+ <branch module="gst-plugins-good/gst-plugins-good-1.4.4.tar.xz" version="1.4.4"
+ repo="gstreamer"
+ hash="sha256:2df90e99da45211c7b2525ae4ac34830a9e7784bd48c072c406c0cf014bdb277"
+ md5sum="673cf9276952bd3937dafd817c9ead2b">
+ </branch>
+ </autotools>
+
+ <autotools id="gst-plugins-bad" autogenargs="--disable-examples --disable-gtk-doc">
+ <dependencies>
+ <dep package="gst-plugins-base"/>
+ </dependencies>
+ <branch module="gst-plugins-bad/gst-plugins-bad-1.4.4.tar.xz" version="1.4.4"
+ repo="gstreamer"
+ hash="sha256:e41b36105c0a13a2cb1ff9f559714e839b82dc3841484cd664790fb7947e55c7"
+ md5sum="972c6e22dd2e44fcf0b04b9d810a56be">
+ <patch file="gst-plugins-bad-remove-gnustep-support.patch" strip="1"/>
+ </branch>
+ </autotools>
+
+ <autotools id="gst-libav" autogenargs="--with-libav-extra-configure='--disable-yasm' --disable-gtk-doc">
+ <dependencies>
+ <dep package="gst-plugins-base"/>
+ </dependencies>
+ <branch module="gst-libav/gst-libav-1.4.4.tar.xz" version="1.4.4"
+ repo="gstreamer"
+ hash="sha256:2ec7285e5ec6731963b0955487cceb9eb04e285ca682f3ef575996c068cde8aa"
+ md5sum="64a3e2cda2687132cadca4efdc63f3b4"/>
+ </autotools>
+
+ <autotools id="xserver" autogenargs="--disable-xinerama --enable-glx --enable-composite --disable-xorg --disable-dmx --disable-xnest --disable-xquartz --disable-xwin --disable-xephyr --disable-xfake --disable-xfbdev --disable-install-setuid --disable-unit-tests --enable-unix-transport --enable-tcp-transport --disable-local-transport --with-xkb-path=/usr/share/X11/xkb --with-xkb-output=/var/lib/xkb --with-xkb-bin-directory=/usr/bin --without-dtrace">
+ <dependencies>
+ <dep package="pixman"/>
+ </dependencies>
+ <branch module="/releases/individual/xserver/xorg-server-1.12.4.tar.gz" version="1.12.4"
+ repo="xorg"
+ hash="sha256:d88225cd3c4a6ecd92d1360b34a0e5b6346e2a04c842c018cef36d8a370714ef"
+ md5sum="19c17bf7ac3e2ce34bc40108692c031f">
+ <patch file="xserver-remove-bogus-dependencies.patch" strip="1"/>
+ <patch file="xserver-fix-glx-init.patch" strip="1"/>
+ </branch>
+ </autotools>
+
+ <autotools id="gtk-doc" autogen-sh="configure">
+ <dependencies>
+ <dep package="glib"/>
+ </dependencies>
+ <branch module="/pub/GNOME/sources/gtk-doc/1.20/gtk-doc-1.20.tar.xz" version="1.20"
+ repo="ftp.gnome.org"
+ hash="sha256:3e6ecf134dbf92a74c24d79848fea3a48e59ab95408a38c6405905d95a293011"/>
+ </autotools>
+
+ <autotools id="mesa" autogenargs="--enable-xlib-glx --disable-dri --with-gallium-drivers=swrast" skip-install="true">
+ <!--- WARNING: At jhbuildrc, when we define the path to the Gallium llvmpipe software rasterizer (needed by XvfbDriver),
+ we assume that the directory is named "Mesa". So, don't change the checkoutdir name even if you update the version. -->
+ <branch module="/pub/mesa/10.5.4/mesa-10.5.4.tar.xz" version="10.5.4"
+ checkoutdir="Mesa"
+ repo="ftp.freedesktop.org"
+ hash="sha256:b51e723f3a20d842c88a92d809435b229fc4744ca0dbec0317d9d4a3ac4c6803">
+ </branch>
+ </autotools>
+
+ <autotools id="libusrsctp" autogen-sh="./bootstrap; ./configure">
+ <branch repo="github.com" module="ossy-szeged/sctp-refimpl.git" checkoutdir="libusrsctp" tag="libusrsctp-r9168c"/>
+ </autotools>
+
+ <autotools id="gst-plugins-openwebrtc" supports-parallel-builds="no" autogen-sh="./autogen.sh; ./configure">
+ <dependencies>
+ <dep package="gst-plugins-base"/>
+ <dep package="libusrsctp"/>
+ </dependencies>
+ <branch repo="github.com" module="EricssonResearch/openwebrtc-gst-plugins.git" checkoutdir="gst-plugins-openwebrtc" tag="e359b67484af90f416ea35e301205d2b53c77a14"/>
+ </autotools>
+
+ <autotools id="libnice">
+ <dependencies>
+ <dep package="gstreamer"/>
+ </dependencies>
+ <branch repo="freedesktop-git" module="libnice/libnice.git" tag="0.1.10" checkoutdir="libnice"/>
+ </autotools>
+
+ <autotools id="openwebrtc" autogenargs="--enable-bridge=no --enable-owr-gst=yes">
+ <dependencies>
+ <dep package="gst-plugins-openwebrtc"/>
+ <dep package="libnice"/>
+ </dependencies>
+ <branch repo="github.com" module="EricssonResearch/openwebrtc.git" checkoutdir="openwebrtc" tag="13516c7f79a0c48bb411464f7613d4b426c70f5b"/>
+ </autotools>
+
+ <!-- Dependencies listed below this point are not thought to affect test results, and are only
+ included because they themselves depend on other dependencies built by jhbuild. -->
+
+ <autotools id="pango" autogen-sh="configure"
+ autogenargs="--enable-cairo">
+ <dependencies>
+ <dep package="cairo"/>
+ <dep package="fontconfig"/>
+ </dependencies>
+ <branch module="/pub/GNOME/sources/pango/1.36/pango-1.36.8.tar.xz" version="1.36.8"
+ repo="ftp.gnome.org"
+ hash="sha256:18dbb51b8ae12bae0ab7a958e7cf3317c9acfc8a1e1103ec2f147164a0fc2d07">
+ </branch>
+ </autotools>
+
+</moduleset>
diff --git a/Tools/gtk/jhbuildrc b/Tools/gtk/jhbuildrc
new file mode 100644
index 000000000..7277daedb
--- /dev/null
+++ b/Tools/gtk/jhbuildrc
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# Copyright (C) 2011-2014 Igalia S.L.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import sys
+import os
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../jhbuild") )
+import jhbuildrc_common
+jhbuildrc_common.init(globals(), "gtk")
+
+__gtk_tools_directory = os.path.abspath(os.path.dirname(__file__))
+sys.path = [__gtk_tools_directory] + sys.path
+import common
+
+# GTK+ 3.0.12 misses the -lm flag when linking the tests.
+module_makeargs['gtk+'] = 'LDFLAGS="-lm" ' + makeargs
+
+# We always enable introspection so that we can sniff out problems with our
+# annotations sooner rather than later.
+autogenargs='--enable-introspection'
+
+if use_lib64:
+ os.environ['LLVMPIPE_LIBGL_PATH'] = os.path.abspath(os.path.join(checkoutroot, 'Mesa', 'lib64', 'gallium'))
+else:
+ os.environ['LLVMPIPE_LIBGL_PATH'] = os.path.abspath(os.path.join(checkoutroot, 'Mesa', 'lib', 'gallium'))
diff --git a/Tools/gtk/make-dist.py b/Tools/gtk/make-dist.py
new file mode 100755
index 000000000..a989b8b38
--- /dev/null
+++ b/Tools/gtk/make-dist.py
@@ -0,0 +1,327 @@
+#!/usr/bin/env python
+# Copyright (C) 2014 Igalia S.L.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+from __future__ import print_function
+from contextlib import closing
+
+import argparse
+import errno
+import multiprocessing
+import os
+import re
+import shutil
+import subprocess
+import tarfile
+
+
+def enum(**enums):
+ return type('Enum', (), enums)
+
+
+class Rule(object):
+ Result = enum(INCLUDE=1, EXCLUDE=2, NO_MATCH=3)
+
+ def __init__(self, type, pattern):
+ self.type = type
+ self.original_pattern = pattern
+ self.pattern = re.compile(pattern)
+
+ def test(self, file):
+ if not(self.pattern.search(file)):
+ return Rule.Result.NO_MATCH
+ return self.type
+
+
+class Ruleset(object):
+ _global_rules = None
+
+ def __init__(self):
+ # By default, accept all files.
+ self.rules = [Rule(Rule.Result.INCLUDE, '.*')]
+
+ @classmethod
+ def global_rules(cls):
+ if not cls._global_rules:
+ cls._global_rules = Ruleset()
+ return cls._global_rules
+
+ @classmethod
+ def add_global_rule(cls, rule):
+ cls.global_rules().add_rule(rule)
+
+ def add_rule(self, rule):
+ self.rules.append(rule)
+
+ def passes(self, file):
+ allowed = False
+ for rule in self.rules:
+ result = rule.test(file)
+ if result == Rule.Result.NO_MATCH:
+ continue
+ allowed = Rule.Result.INCLUDE == result
+ return allowed
+
+
+class File(object):
+ def __init__(self, source_root, tarball_root):
+ self.source_root = source_root
+ self.tarball_root = tarball_root
+
+ def should_skip_file(self, path):
+ # Do not skip files explicitly added from the manifest.
+ return False
+
+ def get_files(self):
+ yield (self.source_root, self.tarball_root)
+
+
+class Directory(object):
+ def __init__(self, source_root, tarball_root):
+ self.source_root = source_root
+ self.tarball_root = tarball_root
+ self.rules = Ruleset()
+
+ self.files_in_version_control = self.list_files_in_version_control()
+
+ def add_rule(self, rule):
+ self.rules.add_rule(rule)
+
+ def get_tarball_path(self, filename):
+ return filename.replace(self.source_root, self.tarball_root, 1)
+
+ def list_files_in_version_control(self):
+ # FIXME: Only git is supported for now.
+ p = subprocess.Popen(['git', 'ls-tree', '-r', '--name-only', 'HEAD', self.source_root], stdout=subprocess.PIPE)
+ out = p.communicate()[0]
+ if not out:
+ return []
+ return out.rstrip('\n').split('\n')
+
+ def should_skip_file(self, path):
+ return path not in self.files_in_version_control
+
+ def get_files(self):
+ for root, dirs, files in os.walk(self.source_root):
+
+ def passes_all_rules(entry):
+ return Ruleset.global_rules().passes(entry) and self.rules.passes(entry)
+
+ to_keep = filter(passes_all_rules, dirs)
+ del dirs[:]
+ dirs.extend(to_keep)
+
+ for file in files:
+ file = os.path.join(root, file)
+ if not passes_all_rules(file):
+ continue
+ yield (file, self.get_tarball_path(file))
+
+
+class Manifest(object):
+ def __init__(self, manifest_filename, source_root, build_root, tarball_root='/'):
+ self.current_directory = None
+ self.directories = []
+ self.tarball_root = tarball_root
+ self.source_root = source_root
+ self.build_root = build_root
+
+ # Normalize the tarball root so that it starts and ends with a slash.
+ if not self.tarball_root.endswith('/'):
+ self.tarball_root = self.tarball_root + '/'
+ if not self.tarball_root.startswith('/'):
+ self.tarball_root = '/' + self.tarball_root
+
+ with open(manifest_filename, 'r') as file:
+ for line in file.readlines():
+ self.process_line(line)
+
+ def add_rule(self, rule):
+ if self.current_directory is not None:
+ self.current_directory.add_rule(rule)
+ else:
+ Ruleset.add_global_rule(rule)
+
+ def add_directory(self, directory):
+ self.current_directory = directory
+ self.directories.append(directory)
+
+ def get_full_source_path(self, source_path):
+ if not os.path.exists(source_path):
+ source_path = os.path.join(self.source_root, source_path)
+ if not os.path.exists(source_path):
+ raise Exception('Could not find directory %s' % source_path)
+ return source_path
+
+ def get_full_tarball_path(self, path):
+ return self.tarball_root + path
+
+ def get_source_and_tarball_paths_from_parts(self, parts):
+ full_source_path = self.get_full_source_path(parts[1])
+ if len(parts) > 2:
+ full_tarball_path = self.get_full_tarball_path(parts[2])
+ else:
+ full_tarball_path = self.get_full_tarball_path(parts[1])
+ return (full_source_path, full_tarball_path)
+
+ def process_line(self, line):
+ parts = line.split()
+ if not parts:
+ return
+ if parts[0].startswith("#"):
+ return
+
+ if parts[0] == "directory" and len(parts) > 1:
+ self.add_directory(Directory(*self.get_source_and_tarball_paths_from_parts(parts)))
+ elif parts[0] == "file" and len(parts) > 1:
+ self.add_directory(File(*self.get_source_and_tarball_paths_from_parts(parts)))
+ elif parts[0] == "exclude" and len(parts) > 1:
+ self.add_rule(Rule(Rule.Result.EXCLUDE, parts[1]))
+ elif parts[0] == "include" and len(parts) > 1:
+ self.add_rule(Rule(Rule.Result.INCLUDE, parts[1]))
+
+ def should_skip_file(self, directory, filename):
+ # Only allow files that are not in version control when they are explicitly included in the manifest from the build dir.
+ if filename.startswith(self.build_root):
+ return False
+
+ return directory.should_skip_file(filename)
+
+ def get_files(self):
+ for directory in self.directories:
+ for file_tuple in directory.get_files():
+ if self.should_skip_file(directory, file_tuple[0]):
+ continue
+ yield file_tuple
+
+ def create_tarfile(self, output):
+ count = 0
+ for file_tuple in self.get_files():
+ count = count + 1
+
+ with closing(tarfile.open(output, 'w')) as tarball:
+ for i, (file_path, tarball_path) in enumerate(self.get_files(), start=1):
+ print('Tarring file {0} of {1}'.format(i, count).ljust(40), end='\r')
+ tarball.add(file_path, tarball_path)
+ print("Wrote {0}".format(output).ljust(40))
+
+
+class Distcheck(object):
+ BUILD_DIRECTORY_NAME = "_build"
+ INSTALL_DIRECTORY_NAME = "_install"
+
+ def __init__(self, source_root, build_root):
+ self.source_root = source_root
+ self.build_root = build_root
+
+ def extract_tarball(self, tarball_path):
+ with closing(tarfile.open(tarball_path, 'r')) as tarball:
+ tarball.extractall(self.build_root)
+
+ def configure(self, dist_dir, build_dir, install_dir):
+ def create_dir(directory, directory_type):
+ try:
+ os.mkdir(directory)
+ except OSError, e:
+ if e.errno != errno.EEXIST or not os.path.isdir(directory):
+ raise Exception("Could not create %s dir at %s: %s" % (directory_type, directory, str(e)))
+
+ create_dir(build_dir, "build")
+ create_dir(install_dir, "install")
+
+ command = ['cmake', '-DPORT=GTK', '-DCMAKE_INSTALL_PREFIX=%s' % install_dir, '-DCMAKE_BUILD_TYPE=Release', dist_dir]
+ subprocess.check_call(command, cwd=build_dir)
+
+ def build(self, build_dir):
+ command = ['make']
+ make_args = os.getenv('MAKE_ARGS')
+ if make_args:
+ command.extend(make_args.split(' '))
+ else:
+ command.append('-j%d' % multiprocessing.cpu_count())
+ subprocess.check_call(command, cwd=build_dir)
+
+ def install(self, build_dir):
+ subprocess.check_call(['make', 'install'], cwd=build_dir)
+
+ def clean(self, dist_dir):
+ shutil.rmtree(dist_dir)
+
+ def check(self, tarball):
+ tarball_name, ext = os.path.splitext(os.path.basename(tarball))
+ dist_dir = os.path.join(self.build_root, tarball_name)
+ build_dir = os.path.join(dist_dir, self.BUILD_DIRECTORY_NAME)
+ install_dir = os.path.join(dist_dir, self.INSTALL_DIRECTORY_NAME)
+
+ self.extract_tarball(tarball)
+ self.configure(dist_dir, build_dir, install_dir)
+ self.build(build_dir)
+ self.install(build_dir)
+ self.clean(dist_dir)
+
+if __name__ == "__main__":
+ class FilePathAction(argparse.Action):
+ def __call__(self, parser, namespace, values, option_string=None):
+ setattr(namespace, self.dest, os.path.abspath(values))
+
+ def ensure_version_if_possible(arguments):
+ if arguments.version is not None:
+ return
+
+ pkgconfig_file = os.path.join(arguments.build_dir, "Source/WebKit2/webkit2gtk-4.0.pc")
+ if os.path.isfile(pkgconfig_file):
+ p = subprocess.Popen(['pkg-config', '--modversion', pkgconfig_file], stdout=subprocess.PIPE)
+ version = p.communicate()[0]
+ if version:
+ arguments.version = version.rstrip('\n')
+
+
+ def get_tarball_root_and_output_filename_from_arguments(arguments):
+ tarball_root = "webkitgtk"
+ if arguments.version is not None:
+ tarball_root += '-' + arguments.version
+
+ output_filename = os.path.join(arguments.build_dir, tarball_root + ".tar")
+ return tarball_root, output_filename
+
+ parser = argparse.ArgumentParser(description='Build a distribution bundle.')
+ parser.add_argument('-c', '--check', action='store_true',
+ help='Check the tarball')
+ parser.add_argument('-s', '--source-dir', type=str, action=FilePathAction, default=os.getcwd(),
+ help='The top-level directory of the source distribution. ' + \
+ 'Directory for relative paths. Defaults to current directory.')
+ parser.add_argument('--version', type=str, default=None,
+ help='The version of the tarball to generate')
+ parser.add_argument('-b', '--build-dir', type=str, action=FilePathAction, default=os.getcwd(),
+ help='The top-level path of directory of the build root. ' + \
+ 'By default is the current directory.')
+ parser.add_argument('manifest_filename', metavar="manifest", type=str, action=FilePathAction, help='The path to the manifest file.')
+
+ arguments = parser.parse_args()
+
+ # Paths in the manifest are relative to the source directory, and this script assumes that
+ # current working directory is the source directory, so change the current working directory
+ # to be the source directory.
+ os.chdir(arguments.source_dir)
+
+ ensure_version_if_possible(arguments)
+ tarball_root, output_filename = get_tarball_root_and_output_filename_from_arguments(arguments)
+
+ manifest = Manifest(arguments.manifest_filename, arguments.source_dir, arguments.build_dir, tarball_root)
+ manifest.create_tarfile(output_filename)
+
+ if arguments.check:
+ Distcheck(arguments.source_dir, arguments.build_dir).check(output_filename)
diff --git a/Tools/gtk/manifest.txt.in b/Tools/gtk/manifest.txt.in
new file mode 100644
index 000000000..e802030cc
--- /dev/null
+++ b/Tools/gtk/manifest.txt.in
@@ -0,0 +1,105 @@
+# Global rules
+exclude #$
+exclude ChangeLog
+exclude Makefile
+exclude PlatformEfl.cmake
+exclude PlatformWin.cmake
+exclude tags$
+exclude ~$
+exclude \.#$
+exclude \.bak$
+exclude \.cproject$
+exclude \.git$
+exclude \.gitattributes$
+exclude \.gitignore$
+exclude \.gyp$
+exclude \.icns$
+exclude \.lproj$
+exclude \.m$
+exclude \.mm$
+exclude \.nib$
+exclude \.o$
+exclude \.order$
+exclude \.orig$
+exclude \.pdf$
+exclude \.plist$
+exclude \.pro\.user$
+exclude \.project$
+exclude \.props$
+exclude \.pyc$
+exclude \.pyo$
+exclude \.rej$
+exclude \.rtf$
+exclude \.sb$
+exclude \.sb\.in$
+exclude \.settings$
+exclude \.svn$
+exclude \.sw[a-p]$
+exclude \.vcxproj$
+exclude \.xib$
+exclude \.xcconfig$
+exclude \.xcodeproj$
+
+# Exclude directories from other ports
+exclude .*\/(mac|ios|cf|cg|cocoa|Cocoa|objc|avfoundation|ca|curl|efl|win)\/.*$
+
+directory Source
+exclude Source/JavaScriptCore/tests
+exclude Source/WebCore/platform/audio/resources
+exclude Source/WebCore/bindings/scripts/test
+exclude Source/WebCore/platform/efl/DefaultTheme
+exclude Source/WebCore/Resources
+exclude Source/cmake/EFLHelpers.cmake$
+exclude Source/cmake/OptionsWinCairo.cmake$
+exclude Source/cmake/OptionsWindows.cmake$
+exclude Source/cmake/OptionsAppleWin.cmake$
+exclude Source/cmake/OptionsEfl.cmake$
+exclude Source/cmake/eflsymbols.filter$
+exclude Source/WebInspectorUI/UserInterface/Images
+
+directory Source/WebInspectorUI/UserInterface/Images/gtk
+
+# Most of the files in Source/WebKit are not necessary to build GTK+ and
+# the directory includes lots of images, so we're a bit more picky here.
+exclude Source/WebKit/.*
+exclude Source/WebKit2/Configurations
+exclude Source/WebKit2/Resources
+exclude Source/WebKit2/gtk/NEWS$
+
+# We do want to include the NEWS, but we want it to be in the root of the archive.
+file Source/WebKit2/gtk/NEWS NEWS
+
+file Source/WebCore/English.lproj/mediaControlsLocalizedStrings.js Source/WebCore/English.lproj/mediaControlsLocalizedStrings.js
+file Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
+
+# Include only the resources we actually build
+file Source/WebCore/Resources/missingImage.png
+file Source/WebCore/Resources/missingImage@2x.png
+file Source/WebCore/Resources/panIcon.png
+file Source/WebCore/Resources/plugIns.js
+file Source/WebCore/Resources/textAreaResizeCorner.png
+file Source/WebCore/Resources/textAreaResizeCorner@2x.png
+file Source/WebCore/Resources/urlIcon.png
+file Source/WebCore/platform/audio/resources/Composite.wav
+
+directory Tools/gtk
+directory Tools/ImageDiff
+directory Tools/MiniBrowser
+directory Tools/TestWebKitAPI
+
+directory Tools/DumpRenderTree
+exclude Tools/DumpRenderTree/fonts
+
+directory Tools/WebKitTestRunner
+exclude Tools/WebKitTestRunner/fonts/
+
+file CMakeLists.txt
+file Tools/CMakeLists.txt
+file Tools/Scripts/VCSUtils.pm
+file Tools/Scripts/run-gtk-tests
+file Tools/Scripts/webkit-build-directory
+file Tools/Scripts/webkitdirs.pm
+file Tools/jhbuild/jhbuildutils.py
+
+directory ${CMAKE_BINARY_DIR}/Documentation/webkit2gtk-${WEBKITGTK_API_VERSION}/html Documentation/webkit2gtk-${WEBKITGTK_API_VERSION}/html
+directory ${CMAKE_BINARY_DIR}/Documentation/webkitdomgtk-${WEBKITGTK_API_VERSION}/html Documentation/webkitdomgtk-${WEBKITGTK_API_VERSION}/html
diff --git a/Tools/gtk/patches/fontconfig-C-11-requires-a-space-between-literal-and-identifier.patch b/Tools/gtk/patches/fontconfig-C-11-requires-a-space-between-literal-and-identifier.patch
new file mode 100644
index 000000000..b4e0a541d
--- /dev/null
+++ b/Tools/gtk/patches/fontconfig-C-11-requires-a-space-between-literal-and-identifier.patch
@@ -0,0 +1,30 @@
+From 7069d717e982adcf8e1d300cbd10eec6322a65c9 Mon Sep 17 00:00:00 2001
+From: Akira TAGOH <akira@tagoh.org>
+Date: Sun, 22 Apr 2012 21:40:44 +0900
+Subject: [PATCH] C++11 requires a space between literal and identifier
+
+Reported by Buganini
+---
+ fontconfig/fontconfig.h | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h
+index 0e2ca50..b27ccb5 100644
+--- a/fontconfig/fontconfig.h
++++ b/fontconfig/fontconfig.h
+@@ -112,9 +112,9 @@ typedef int FcBool;
+ #define FC_DECORATIVE "decorative" /* Bool - true if style is a decorative variant */
+ #define FC_LCD_FILTER "lcdfilter" /* Int */
+
+-#define FC_CACHE_SUFFIX ".cache-"FC_CACHE_VERSION
+-#define FC_DIR_CACHE_FILE "fonts.cache-"FC_CACHE_VERSION
+-#define FC_USER_CACHE_FILE ".fonts.cache-"FC_CACHE_VERSION
++#define FC_CACHE_SUFFIX ".cache-" FC_CACHE_VERSION
++#define FC_DIR_CACHE_FILE "fonts.cache-" FC_CACHE_VERSION
++#define FC_USER_CACHE_FILE ".fonts.cache-" FC_CACHE_VERSION
+
+ /* Adjust outline rasterizer */
+ #define FC_CHAR_WIDTH "charwidth" /* Int */
+--
+1.8.3.2
+
diff --git a/Tools/gtk/patches/freetype6-2.4.11-truetype-font-height-fix.patch b/Tools/gtk/patches/freetype6-2.4.11-truetype-font-height-fix.patch
new file mode 100644
index 000000000..0ffe76b4e
--- /dev/null
+++ b/Tools/gtk/patches/freetype6-2.4.11-truetype-font-height-fix.patch
@@ -0,0 +1,39 @@
+From e0469372be3870a5ad60b2c4586e9c281357bd28 Mon Sep 17 00:00:00 2001
+From: Werner Lemberg <wl@gnu.org>
+Date: Tue, 22 Jan 2013 10:07:07 +0000
+Subject: [truetype] Fix font height.
+
+* src/truetype/ttobjs.c (tt_size_reset): The Windows rendering
+engine uses rounded values of the ascender and descender to compute
+the TrueType font height.
+---
+diff --git a/src/truetype/ttobjs.c b/src/truetype/ttobjs.c
+index c61b218..590b66c 100644
+--- a/src/truetype/ttobjs.c
++++ b/src/truetype/ttobjs.c
+@@ -4,7 +4,7 @@
+ /* */
+ /* Objects manager (body). */
+ /* */
+-/* Copyright 1996-2012 */
++/* Copyright 1996-2013 */
+ /* David Turner, Robert Wilhelm, and Werner Lemberg. */
+ /* */
+ /* This file is part of the FreeType project, and may only be used, */
+@@ -1177,11 +1177,12 @@
+ FT_PIX_ROUND( FT_MulFix( face->root.ascender, metrics->y_scale ) );
+ metrics->descender =
+ FT_PIX_ROUND( FT_MulFix( face->root.descender, metrics->y_scale ) );
+- metrics->height =
+- FT_PIX_ROUND( FT_MulFix( face->root.height, metrics->y_scale ) );
+ metrics->max_advance =
+ FT_PIX_ROUND( FT_MulFix( face->root.max_advance_width,
+ metrics->x_scale ) );
++
++ /* the height is derived from rounded values */
++ metrics->height = metrics->ascender - metrics->descender;
+ }
+
+ /* compute new transformation */
+--
+cgit v0.9.0.2
diff --git a/Tools/gtk/patches/glib-warning-fix.patch b/Tools/gtk/patches/glib-warning-fix.patch
new file mode 100644
index 000000000..8faf4d11f
--- /dev/null
+++ b/Tools/gtk/patches/glib-warning-fix.patch
@@ -0,0 +1,34 @@
+From 9f90ee5eeccd47f39c7a03dcd786b125a19c195d Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@gnome.org>
+Date: Sat, 13 Jun 2015 22:52:33 -0500
+Subject: [PATCH] genmarshal: silence register storage class warnings
+
+Using the register keyword triggers warnings on noteworthy compilers
+(clang), since it's deprecated in C++ and at danger of being removed
+from the language. There is no reason to use it since it isn't 1980
+anymore.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=750918
+---
+ gobject/glib-genmarshal.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/gobject/glib-genmarshal.c b/gobject/glib-genmarshal.c
+index be4151a..ca78a6f 100644
+--- a/gobject/glib-genmarshal.c
++++ b/gobject/glib-genmarshal.c
+@@ -412,9 +412,9 @@ generate_marshal (const gchar *signame,
+ g_fprintf (fout, "%s%s data2);\n", indent (ind), pad ("gpointer"));
+
+ /* cfile marshal variables */
+- g_fprintf (fout, " register GMarshalFunc_%s callback;\n", signame);
+- g_fprintf (fout, " register GCClosure *cc = (GCClosure*) closure;\n");
+- g_fprintf (fout, " register gpointer data1, data2;\n");
++ g_fprintf (fout, " GMarshalFunc_%s callback;\n", signame);
++ g_fprintf (fout, " GCClosure *cc = (GCClosure*) closure;\n");
++ g_fprintf (fout, " gpointer data1, data2;\n");
+ if (sig->rarg->setter)
+ g_fprintf (fout, " %s v_return;\n", sig->rarg->ctype);
+
+--
+2.4.2 \ No newline at end of file
diff --git a/Tools/gtk/patches/gst-plugins-bad-remove-gnustep-support.patch b/Tools/gtk/patches/gst-plugins-bad-remove-gnustep-support.patch
new file mode 100644
index 000000000..fb2ab2509
--- /dev/null
+++ b/Tools/gtk/patches/gst-plugins-bad-remove-gnustep-support.patch
@@ -0,0 +1,325 @@
+From 13c8517570c3550b5c46a3cb0ff7f8b7888a4ddf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
+Date: Thu, 13 Nov 2014 11:58:07 +0100
+Subject: gl/cocoa: Remove GNUStep support
+
+Until gcc and GNUStep properly support Objective-C blocks and other
+"new" features of Objective-C we can't properly support them without
+making the code much more ugly.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=739152
+
+diff --git a/configure.ac b/configure.ac
+index fdb6c88..120cd44 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -611,7 +611,7 @@ AC_ARG_ENABLE([glx],
+ esac],[NEED_GLX=auto])
+
+ AC_ARG_ENABLE([cocoa],
+- [ --enable-cocoa Enable Cocoa support (using GNUstep on non OS X platforms) @<:@default=auto@:>@],
++ [ --enable-cocoa Enable Cocoa support @<:@default=auto@:>@],
+ [case "${enableval}" in
+ yes) NEED_COCOA=yes ;;
+ no) NEED_COCOA=no ;;
+@@ -653,7 +653,6 @@ save_LIBS="$LIBS"
+ HAVE_GL=no
+ HAVE_GLES2=no
+ HAVE_GLU=no
+-HAVE_GNUSTEP_COCOA=no
+ HAVE_WAYLAND_EGL=no
+
+ HAVE_EGL_RPI=no
+@@ -744,35 +743,6 @@ case $host in
+ ;;
+ esac
+
+-case $host in
+- *-darwin* ) ;;
+- *)
+- AC_PATH_PROG([GNUSTEP_CONFIG],[gnustep-config])
+- if test "x$GNUSTEP_CONFIG" != "x"; then
+- GNUSTEP_HOST="`$GNUSTEP_CONFIG --variable=GNUSTEP_HOST`"
+- case $host in
+- *-mingw* )
+- case $GNUSTEP_HOST in
+- *-mingw* )
+- HAVE_GNUSTEP_COCOA=yes
+- ;;
+- esac
+- ;;
+- * )
+- HAVE_GNUSTEP_COCOA=yes
+- ;;
+- esac
+- fi
+- ;;
+-esac
+-
+-AC_MSG_CHECKING([for GNUstep])
+-if test "x$HAVE_GNUSTEP_COCOA" = "xyes" ; then
+- AC_MSG_RESULT([yes])
+-else
+- AC_MSG_RESULT([no])
+-fi
+-
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+
+@@ -1009,19 +979,6 @@ case $host in
+ fi
+ fi
+ fi
+-
+- dnl GNUstep provides the Cocoa API on unix
+- if test "x$NEED_COCOA" != "xno"; then
+- if test "x$HAVE_GNUSTEP_COCOA" = "xyes" ; then
+- GNUSTEP_OBJCFLAGS="`$GNUSTEP_CONFIG --objc-flags`"
+- GNUSTEP_LIBS="`$GNUSTEP_CONFIG --gui-libs`"
+- GL_LIBS="$GL_LIBS $GNUSTEP_LIBS -lgnustep-gui -lgnustep-base"
+- GL_OBJCFLAGS="$GL_OBJCFLAGS $GNUSTEP_OBJCFLAGS"
+-
+- USE_COCOA=yes
+- HAVE_WINDOW_COCOA=yes
+- fi
+- fi
+ ;;
+ *-darwin*)
+ if test "x$HAVE_IOS" = "xyes"; then
+@@ -1114,17 +1071,6 @@ case $host in
+ fi
+ fi
+ fi
+-
+- dnl GNUstep provides the Cocoa API on win32
+- if test "x$HAVE_GNUSTEP_COCOA" = "xyes" ; then
+- GNUSTEP_CFLAGS="`$GNUSTEP_CONFIG --objc-flags`"
+- GNUSTEP_LIBS="`$GNUSTEP_CONFIG --gui-libs`"
+- GL_LIBS="$GL_LIBS $GNUSTEP_LIBS -lgnustep-gui -lgnustep-base"
+- GL_OBJCFLAGS="$OBJCFLAGS $GNUSTEP_CFLAGS"
+-
+- USE_COCOA=yes
+- HAVE_WINDOW_COCOA=yes
+- fi
+ ;;
+ *)
+ AC_MSG_WARN([Don't know how to check for OpenGL on your platform.])
+diff --git a/gst-libs/gst/gl/cocoa/gstglcontext_cocoa.m b/gst-libs/gst/gl/cocoa/gstglcontext_cocoa.m
+index 94da0bc..84e3a99 100644
+--- a/gst-libs/gst/gl/cocoa/gstglcontext_cocoa.m
++++ b/gst-libs/gst/gl/cocoa/gstglcontext_cocoa.m
+@@ -40,7 +40,6 @@ static GstGLPlatform gst_gl_context_cocoa_get_gl_platform (GstGLContext * contex
+
+ G_DEFINE_TYPE (GstGLContextCocoa, gst_gl_context_cocoa, GST_GL_TYPE_CONTEXT);
+
+-#ifndef GNUSTEP
+ static GMutex nsapp_lock;
+ static GCond nsapp_cond;
+
+@@ -96,16 +95,12 @@ gst_gl_window_cocoa_nsapp_iteration (gpointer data)
+
+ return TRUE;
+ }
+-#endif
+
+ static void
+ gst_gl_context_cocoa_class_init (GstGLContextCocoaClass * klass)
+ {
+ GstGLContextClass *context_class = (GstGLContextClass *) klass;
+-
+-#ifndef GNUSTEP
+ NSAutoreleasePool* pool = nil;
+-#endif
+
+ g_type_class_add_private (klass, sizeof (GstGLContextCocoaPrivate));
+
+@@ -121,7 +116,6 @@ gst_gl_context_cocoa_class_init (GstGLContextCocoaClass * klass)
+ context_class->get_gl_platform =
+ GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_get_gl_platform);
+
+-#ifndef GNUSTEP
+ pool = [[NSAutoreleasePool alloc] init];
+
+ /* [NSApplication sharedApplication] will usually be
+@@ -191,7 +185,6 @@ gst_gl_context_cocoa_class_init (GstGLContextCocoaClass * klass)
+ }
+
+ [pool release];
+-#endif
+ }
+
+ static void
+@@ -219,9 +212,7 @@ gst_gl_context_cocoa_create_context (GstGLContext *context, GstGLAPI gl_api,
+ GstGLWindowCocoa *window_cocoa = GST_GL_WINDOW_COCOA (window);
+ __block NSOpenGLContext *glContext = nil;
+
+-#ifndef GNUSTEP
+ priv->source_id = g_timeout_add (200, gst_gl_window_cocoa_nsapp_iteration, NULL);
+-#endif
+
+ priv->gl_context = nil;
+ if (other_context)
+@@ -243,9 +234,6 @@ gst_gl_context_cocoa_create_context (GstGLContext *context, GstGLAPI gl_api,
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+-#ifdef GNUSTEP
+- [NSApplication sharedApplication];
+-#endif
+ rect.origin.x = 0;
+ rect.origin.y = 0;
+ rect.size.width = 320;
+@@ -265,7 +253,6 @@ gst_gl_context_cocoa_create_context (GstGLContext *context, GstGLAPI gl_api,
+
+ [window_handle setContentView:glView];
+
+-#ifndef GNUSTEP
+ glContext = [[NSOpenGLContext alloc] initWithFormat:fmt
+ shareContext:context_cocoa->priv->external_gl_context];
+
+@@ -275,10 +262,6 @@ gst_gl_context_cocoa_create_context (GstGLContext *context, GstGLAPI gl_api,
+
+ [glContext setView:glView];
+
+-#else
+- /* FIXME try to make context sharing work in GNUstep */
+- context_cocoa->priv->gl_context = glContext;
+-#endif
+ [pool release];
+ });
+
+@@ -300,11 +283,7 @@ gst_gl_context_cocoa_create_context (GstGLContext *context, GstGLAPI gl_api,
+ */
+ NS_DURING {
+ if (glContext) {
+-#ifdef GNUSTEP
+- const long swapInterval = 1;
+-#else
+ const GLint swapInterval = 1;
+-#endif
+ [glContext setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
+ }
+ } NS_HANDLER {
+diff --git a/gst-libs/gst/gl/cocoa/gstglwindow_cocoa.m b/gst-libs/gst/gl/cocoa/gstglwindow_cocoa.m
+index 0771662..d776477 100644
+--- a/gst-libs/gst/gl/cocoa/gstglwindow_cocoa.m
++++ b/gst-libs/gst/gl/cocoa/gstglwindow_cocoa.m
+@@ -257,11 +257,9 @@ draw_cb (gpointer data)
+ x += 20;
+ y += 20;
+
+-#ifndef GNUSTEP
+ [priv->internal_win_id setFrame:windowRect display:NO];
+ GST_DEBUG ("make the window available\n");
+ [priv->internal_win_id makeMainWindow];
+-#endif
+
+ [priv->internal_win_id orderFrontRegardless];
+
+@@ -457,17 +455,6 @@ close_window_cb (gpointer data)
+ }
+
+ - (void) applicationWillTerminate:(NSNotification *)aNotification {
+-#ifdef GNUSTEP
+- /* fixes segfault with gst-launch-1.0 -e ... and sending SIGINT (Ctrl-C)
+- * which causes GNUstep to run a signal handler in the main thread.
+- * However that thread has never been 'registered' with GNUstep so
+- * the autorelease magic of objective-c causes a segfault from accessing
+- * a null NSThread object somewhere deep in GNUstep.
+- *
+- * I put it here because this is the first time we can register the thread.
+- */
+- GSRegisterCurrentThread();
+-#endif
+ }
+
+ @end
+@@ -488,9 +475,7 @@ close_window_cb (gpointer data)
+
+ window_cocoa = window;
+
+-#ifndef GNUSTEP
+ [self setWantsLayer:NO];
+-#endif
+
+ /* Get notified about changes */
+ [self setPostsFrameChangedNotifications:YES];
+diff --git a/tests/examples/gl/cocoa/cocoa-videooverlay.m b/tests/examples/gl/cocoa/cocoa-videooverlay.m
+index 683abe1..9c5fc9a 100755
+--- a/tests/examples/gl/cocoa/cocoa-videooverlay.m
++++ b/tests/examples/gl/cocoa/cocoa-videooverlay.m
+@@ -136,15 +136,8 @@ static void end_stream_cb(GstBus* bus, GstMessage* message, MainWindow* window)
+
+ static gpointer thread_func (MainWindow* window)
+ {
+-#ifdef GNUSTEP
+- GSRegisterCurrentThread();
+-#endif
+-
+ g_main_loop_run ([window loop]);
+
+-#ifdef GNUSTEP
+- GSUnregisterCurrentThread();
+-#endif
+ return NULL;
+ }
+
+@@ -172,10 +165,6 @@ int main(int argc, char **argv)
+ NSAutoreleasePool *pool=nil;
+ NSRect rect;
+ MainWindow *window=nil;
+-
+-#ifdef GNUSTEP
+- GstState state;
+-#endif
+
+ g_print("app created\n");
+
+@@ -203,18 +192,8 @@ int main(int argc, char **argv)
+ if (!ok)
+ g_warning("could not link videosrc to videosink\n");
+
+-#ifdef GNUSTEP
+- gst_element_set_state (pipeline, GST_STATE_PAUSED);
+- state = GST_STATE_PAUSED;
+- gst_element_get_state (pipeline, &state, &state, GST_CLOCK_TIME_NONE);
+- g_print("pipeline paused\n");
+- GSRegisterCurrentThread();
+-#endif
+-
+ pool = [[NSAutoreleasePool alloc] init];
+-#ifndef GNUSTEP
+ [NSApplication sharedApplication];
+-#endif
+
+ rect.origin.x = 0; rect.origin.y = 0;
+ rect.size.width = width; rect.size.height = height;
+@@ -236,7 +215,6 @@ int main(int argc, char **argv)
+
+ [window orderFront:window];
+
+-#ifndef GNUSTEP
+ while (![window isClosed]) {
+ NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate dateWithTimeIntervalSinceNow:1]
+@@ -244,7 +222,6 @@ int main(int argc, char **argv)
+ if (event)
+ [NSApp sendEvent:event];
+ }
+-#endif
+
+ g_thread_join (loop_thread);
+
+@@ -252,9 +229,5 @@ int main(int argc, char **argv)
+
+ [pool release];
+
+-#ifdef GNUSTEP
+- GSUnregisterCurrentThread();
+-#endif
+-
+ return 0;
+ }
+--
+2.0.0.rc2
+
diff --git a/Tools/gtk/patches/gst-plugins-base-rtp-rtcpbuffer-fix-typo-in-enum.patch b/Tools/gtk/patches/gst-plugins-base-rtp-rtcpbuffer-fix-typo-in-enum.patch
new file mode 100644
index 000000000..e3ddb7238
--- /dev/null
+++ b/Tools/gtk/patches/gst-plugins-base-rtp-rtcpbuffer-fix-typo-in-enum.patch
@@ -0,0 +1,45 @@
+From dfc34c58411f50b37b2e1300560ae8a0b6a9a7d4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= <tim@centricular.com>
+Date: Tue, 7 Apr 2015 16:43:59 +0100
+Subject: [PATCH] rtp: rtcpbuffer: fix typo in enum
+
+and in docs. Spotted by Rob Swain.
+---
+ gst-libs/gst/rtp/gstrtcpbuffer.h | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/gst-libs/gst/rtp/gstrtcpbuffer.h b/gst-libs/gst/rtp/gstrtcpbuffer.h
+index b5ff4a1..47378cf 100644
+--- a/gst-libs/gst/rtp/gstrtcpbuffer.h
++++ b/gst-libs/gst/rtp/gstrtcpbuffer.h
+@@ -59,6 +59,9 @@ typedef enum
+ GST_RTCP_TYPE_PSFB = 206
+ } GstRTCPType;
+
++/* FIXME 2.0: backwards compatibility define for enum typo */
++#define GST_RTCP_RTPFB_TYPE_RCTP_SR_REQ GST_RTCP_RTPFB_TYPE_RTCP_SR_REQ
++
+ /**
+ * GstRTCPFBType:
+ * @GST_RTCP_FB_TYPE_INVALID: Invalid type
+@@ -66,7 +69,7 @@ typedef enum
+ * @GST_RTCP_RTPFB_TYPE_TMMBR: Temporary Maximum Media Stream Bit Rate Request
+ * @GST_RTCP_RTPFB_TYPE_TMMBN: Temporary Maximum Media Stream Bit Rate
+ * Notification
+- * @GST_RTCP_RTPFB_TYPE_RTCP_SR_SEQ: Request an SR packet for early
++ * @GST_RTCP_RTPFB_TYPE_RTCP_SR_REQ: Request an SR packet for early
+ * synchronization
+ * @GST_RTCP_PSFB_TYPE_PLI: Picture Loss Indication
+ * @GST_RTCP_PSFB_TYPE_SLI: Slice Loss Indication
+@@ -89,7 +92,7 @@ typedef enum
+ GST_RTCP_RTPFB_TYPE_TMMBR = 3,
+ GST_RTCP_RTPFB_TYPE_TMMBN = 4,
+ /* RTPFB types assigned in RFC 6051 */
+- GST_RTCP_RTPFB_TYPE_RCTP_SR_REQ = 5,
++ GST_RTCP_RTPFB_TYPE_RTCP_SR_REQ = 5,
+ /* PSFB types */
+ GST_RTCP_PSFB_TYPE_PLI = 1,
+ GST_RTCP_PSFB_TYPE_SLI = 2,
+--
+2.1.4
+
diff --git a/Tools/gtk/patches/librsvg-2.36.1-bump-up-config.guess-to-support-aarch64.patch b/Tools/gtk/patches/librsvg-2.36.1-bump-up-config.guess-to-support-aarch64.patch
new file mode 100644
index 000000000..a20af4ce1
--- /dev/null
+++ b/Tools/gtk/patches/librsvg-2.36.1-bump-up-config.guess-to-support-aarch64.patch
@@ -0,0 +1,1581 @@
+diff -ur librsvg-2.36.1-orig/config.guess librsvg-2.36.1/config.guess
+--- librsvg-2.36.1-orig/config.guess 2012-02-03 13:14:58.000000000 +0100
++++ librsvg-2.36.1/config.guess 2014-09-25 18:36:54.000000000 +0200
+@@ -1,14 +1,12 @@
+ #! /bin/sh
+ # Attempt to guess a canonical system name.
+-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+-# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+-# Free Software Foundation, Inc.
++# Copyright 1992-2014 Free Software Foundation, Inc.
+
+-timestamp='2009-11-20'
++timestamp='2014-03-23'
+
+ # This file is free software; you can redistribute it and/or modify it
+ # under the terms of the GNU General Public License as published by
+-# the Free Software Foundation; either version 2 of the License, or
++# the Free Software Foundation; either version 3 of the License, or
+ # (at your option) any later version.
+ #
+ # This program is distributed in the hope that it will be useful, but
+@@ -17,26 +15,22 @@
+ # General Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License
+-# along with this program; if not, write to the Free Software
+-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+-# 02110-1301, USA.
++# along with this program; if not, see <http://www.gnu.org/licenses/>.
+ #
+ # As a special exception to the GNU General Public License, if you
+ # distribute this file as part of a program that contains a
+ # configuration script generated by Autoconf, you may include it under
+-# the same distribution terms that you use for the rest of that program.
+-
+-
+-# Originally written by Per Bothner. Please send patches (context
+-# diff format) to <config-patches@gnu.org> and include a ChangeLog
+-# entry.
++# the same distribution terms that you use for the rest of that
++# program. This Exception is an additional permission under section 7
++# of the GNU General Public License, version 3 ("GPLv3").
+ #
+-# This script attempts to guess a canonical system name similar to
+-# config.sub. If it succeeds, it prints the system name on stdout, and
+-# exits with 0. Otherwise, it exits with 1.
++# Originally written by Per Bothner.
+ #
+ # You can get the latest version of this script from:
+ # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
++#
++# Please send patches with a ChangeLog entry to config-patches@gnu.org.
++
+
+ me=`echo "$0" | sed -e 's,.*/,,'`
+
+@@ -56,8 +50,7 @@
+ GNU config.guess ($timestamp)
+
+ Originally written by Per Bothner.
+-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+-2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
++Copyright 1992-2014 Free Software Foundation, Inc.
+
+ This is free software; see the source for copying conditions. There is NO
+ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+@@ -139,12 +132,33 @@
+ UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+ UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
++case "${UNAME_SYSTEM}" in
++Linux|GNU|GNU/*)
++ # If the system lacks a compiler, then just pick glibc.
++ # We could probably try harder.
++ LIBC=gnu
++
++ eval $set_cc_for_build
++ cat <<-EOF > $dummy.c
++ #include <features.h>
++ #if defined(__UCLIBC__)
++ LIBC=uclibc
++ #elif defined(__dietlibc__)
++ LIBC=dietlibc
++ #else
++ LIBC=gnu
++ #endif
++ EOF
++ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
++ ;;
++esac
++
+ # Note: order is significant - the case branches are not exclusive.
+
+ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+- # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
++ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+@@ -180,7 +194,7 @@
+ fi
+ ;;
+ *)
+- os=netbsd
++ os=netbsd
+ ;;
+ esac
+ # The OS release
+@@ -201,6 +215,10 @@
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
++ *:Bitrig:*:*)
++ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
++ echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
++ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+@@ -223,7 +241,7 @@
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
++ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+@@ -269,7 +287,10 @@
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+- exit ;;
++ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
++ exitcode=$?
++ trap '' 0
++ exit $exitcode ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+@@ -295,12 +316,12 @@
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+- echo powerpc-ibm-os400
++ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+- arm:riscos:*:*|arm:RISCOS:*:*)
++ arm*:riscos:*:*|arm*:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+@@ -394,23 +415,23 @@
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+- echo m68k-atari-mint${UNAME_RELEASE}
++ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+- exit ;;
++ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+- echo m68k-atari-mint${UNAME_RELEASE}
++ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+- echo m68k-milan-mint${UNAME_RELEASE}
+- exit ;;
++ echo m68k-milan-mint${UNAME_RELEASE}
++ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+- echo m68k-hades-mint${UNAME_RELEASE}
+- exit ;;
++ echo m68k-hades-mint${UNAME_RELEASE}
++ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+- echo m68k-unknown-mint${UNAME_RELEASE}
+- exit ;;
++ echo m68k-unknown-mint${UNAME_RELEASE}
++ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+@@ -480,8 +501,8 @@
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+- # DG/UX returns AViiON for all architectures
+- UNAME_PROCESSOR=`/usr/bin/uname -p`
++ # DG/UX returns AViiON for all architectures
++ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+@@ -494,7 +515,7 @@
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+- exit ;;
++ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+@@ -551,7 +572,7 @@
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+- *:AIX:*:[456])
++ *:AIX:*:[4567])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+@@ -594,52 +615,52 @@
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+- sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+- case "${sc_cpu_version}" in
+- 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+- 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+- 532) # CPU_PA_RISC2_0
+- case "${sc_kernel_bits}" in
+- 32) HP_ARCH="hppa2.0n" ;;
+- 64) HP_ARCH="hppa2.0w" ;;
++ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
++ case "${sc_cpu_version}" in
++ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
++ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
++ 532) # CPU_PA_RISC2_0
++ case "${sc_kernel_bits}" in
++ 32) HP_ARCH="hppa2.0n" ;;
++ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+- esac ;;
+- esac
++ esac ;;
++ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+- sed 's/^ //' << EOF >$dummy.c
++ sed 's/^ //' << EOF >$dummy.c
++
++ #define _HPUX_SOURCE
++ #include <stdlib.h>
++ #include <unistd.h>
++
++ int main ()
++ {
++ #if defined(_SC_KERNEL_BITS)
++ long bits = sysconf(_SC_KERNEL_BITS);
++ #endif
++ long cpu = sysconf (_SC_CPU_VERSION);
+
+- #define _HPUX_SOURCE
+- #include <stdlib.h>
+- #include <unistd.h>
+-
+- int main ()
+- {
+- #if defined(_SC_KERNEL_BITS)
+- long bits = sysconf(_SC_KERNEL_BITS);
+- #endif
+- long cpu = sysconf (_SC_CPU_VERSION);
+-
+- switch (cpu)
+- {
+- case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+- case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+- case CPU_PA_RISC2_0:
+- #if defined(_SC_KERNEL_BITS)
+- switch (bits)
+- {
+- case 64: puts ("hppa2.0w"); break;
+- case 32: puts ("hppa2.0n"); break;
+- default: puts ("hppa2.0"); break;
+- } break;
+- #else /* !defined(_SC_KERNEL_BITS) */
+- puts ("hppa2.0"); break;
+- #endif
+- default: puts ("hppa1.0"); break;
+- }
+- exit (0);
+- }
++ switch (cpu)
++ {
++ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
++ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
++ case CPU_PA_RISC2_0:
++ #if defined(_SC_KERNEL_BITS)
++ switch (bits)
++ {
++ case 64: puts ("hppa2.0w"); break;
++ case 32: puts ("hppa2.0n"); break;
++ default: puts ("hppa2.0"); break;
++ } break;
++ #else /* !defined(_SC_KERNEL_BITS) */
++ puts ("hppa2.0"); break;
++ #endif
++ default: puts ("hppa1.0"); break;
++ }
++ exit (0);
++ }
+ EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+@@ -730,22 +751,22 @@
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+- exit ;;
++ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+- exit ;;
++ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+- exit ;;
++ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+- exit ;;
++ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+- exit ;;
++ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+@@ -769,14 +790,14 @@
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+- FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+- FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+- echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+- exit ;;
++ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
++ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
++ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
++ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+- FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+- FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+- echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
++ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
++ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
++ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+@@ -788,30 +809,35 @@
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+- case ${UNAME_MACHINE} in
+- pc98)
+- echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
++ UNAME_PROCESSOR=`/usr/bin/uname -p`
++ case ${UNAME_PROCESSOR} in
+ amd64)
+ echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ *)
+- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
++ echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ esac
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
++ *:MINGW64*:*)
++ echo ${UNAME_MACHINE}-pc-mingw64
++ exit ;;
+ *:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
++ *:MSYS*:*)
++ echo ${UNAME_MACHINE}-pc-msys
++ exit ;;
+ i*:windows32*:*)
+- # uname -m includes "-pc" on this system.
+- echo ${UNAME_MACHINE}-mingw32
++ # uname -m includes "-pc" on this system.
++ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ *:Interix*:*)
+- case ${UNAME_MACHINE} in
++ case ${UNAME_MACHINE} in
+ x86)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+@@ -848,15 +874,22 @@
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+- echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
++ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+- echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
++ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
++ aarch64:Linux:*:*)
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
++ exit ;;
++ aarch64_be:Linux:*:*)
++ UNAME_MACHINE=aarch64_be
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
++ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+@@ -866,52 +899,56 @@
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+- esac
++ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+- if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+- echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
++ if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
++ exit ;;
++ arc:Linux:*:* | arceb:Linux:*:*)
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+- echo ${UNAME_MACHINE}-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ else
+- echo ${UNAME_MACHINE}-unknown-linux-gnueabi
++ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
++ | grep -q __ARM_PCS_VFP
++ then
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
++ else
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
++ fi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+- echo ${UNAME_MACHINE}-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ cris:Linux:*:*)
+- echo cris-axis-linux-gnu
++ echo ${UNAME_MACHINE}-axis-linux-${LIBC}
+ exit ;;
+ crisv32:Linux:*:*)
+- echo crisv32-axis-linux-gnu
++ echo ${UNAME_MACHINE}-axis-linux-${LIBC}
+ exit ;;
+ frv:Linux:*:*)
+- echo frv-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
++ exit ;;
++ hexagon:Linux:*:*)
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ i*86:Linux:*:*)
+- LIBC=gnu
+- eval $set_cc_for_build
+- sed 's/^ //' << EOF >$dummy.c
+- #ifdef __dietlibc__
+- LIBC=dietlibc
+- #endif
+-EOF
+- eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+- echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
++ echo ${UNAME_MACHINE}-pc-linux-${LIBC}
+ exit ;;
+ ia64:Linux:*:*)
+- echo ${UNAME_MACHINE}-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ m32r*:Linux:*:*)
+- echo ${UNAME_MACHINE}-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ m68*:Linux:*:*)
+- echo ${UNAME_MACHINE}-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ eval $set_cc_for_build
+@@ -930,51 +967,63 @@
+ #endif
+ EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+- test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
++ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
+ ;;
+- or32:Linux:*:*)
+- echo or32-unknown-linux-gnu
++ openrisc*:Linux:*:*)
++ echo or1k-unknown-linux-${LIBC}
++ exit ;;
++ or32:Linux:*:* | or1k*:Linux:*:*)
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ padre:Linux:*:*)
+- echo sparc-unknown-linux-gnu
++ echo sparc-unknown-linux-${LIBC}
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+- echo hppa64-unknown-linux-gnu
++ echo hppa64-unknown-linux-${LIBC}
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+- PA7*) echo hppa1.1-unknown-linux-gnu ;;
+- PA8*) echo hppa2.0-unknown-linux-gnu ;;
+- *) echo hppa-unknown-linux-gnu ;;
++ PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
++ PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
++ *) echo hppa-unknown-linux-${LIBC} ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+- echo powerpc64-unknown-linux-gnu
++ echo powerpc64-unknown-linux-${LIBC}
+ exit ;;
+ ppc:Linux:*:*)
+- echo powerpc-unknown-linux-gnu
++ echo powerpc-unknown-linux-${LIBC}
++ exit ;;
++ ppc64le:Linux:*:*)
++ echo powerpc64le-unknown-linux-${LIBC}
++ exit ;;
++ ppcle:Linux:*:*)
++ echo powerpcle-unknown-linux-${LIBC}
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+- echo ${UNAME_MACHINE}-ibm-linux
++ echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
+ exit ;;
+ sh64*:Linux:*:*)
+- echo ${UNAME_MACHINE}-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ sh*:Linux:*:*)
+- echo ${UNAME_MACHINE}-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+- echo ${UNAME_MACHINE}-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
++ exit ;;
++ tile*:Linux:*:*)
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ vax:Linux:*:*)
+- echo ${UNAME_MACHINE}-dec-linux-gnu
++ echo ${UNAME_MACHINE}-dec-linux-${LIBC}
+ exit ;;
+ x86_64:Linux:*:*)
+- echo x86_64-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ xtensa*:Linux:*:*)
+- echo ${UNAME_MACHINE}-unknown-linux-gnu
++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+@@ -983,11 +1032,11 @@
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+- # Unixware is an offshoot of SVR4, but it has its own version
+- # number series starting with 2...
+- # I am not positive that other SVR4 systems won't match this,
++ # Unixware is an offshoot of SVR4, but it has its own version
++ # number series starting with 2...
++ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+- # Use sysv4.2uw... so that sysv4* matches it.
++ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+@@ -1019,7 +1068,7 @@
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+- # UnixWare 7.x, OpenUNIX and OpenServer 6.
++ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+@@ -1047,13 +1096,13 @@
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+- # uname -m prints for DJGPP always 'pc', but it prints nothing about
+- # the processor, so we play safe by assuming i586.
++ # uname -m prints for DJGPP always 'pc', but it prints nothing about
++ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+- exit ;;
++ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+@@ -1088,8 +1137,8 @@
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+- /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+- && { echo i486-ncr-sysv4; exit; } ;;
++ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
++ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+@@ -1132,10 +1181,10 @@
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+- # says <Richard.M.Bartel@ccMail.Census.GOV>
+- echo i586-unisys-sysv4
+- exit ;;
++ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
++ # says <Richard.M.Bartel@ccMail.Census.GOV>
++ echo i586-unisys-sysv4
++ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+@@ -1161,11 +1210,11 @@
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+- echo mips-nec-sysv${UNAME_RELEASE}
++ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+- echo mips-unknown-sysv${UNAME_RELEASE}
++ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+- exit ;;
++ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+@@ -1178,6 +1227,9 @@
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
++ x86_64:Haiku:*:*)
++ echo x86_64-unknown-haiku
++ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+@@ -1204,19 +1256,31 @@
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+- case $UNAME_PROCESSOR in
+- i386)
+- eval $set_cc_for_build
+- if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+- if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+- (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+- grep IS_64BIT_ARCH >/dev/null
+- then
+- UNAME_PROCESSOR="x86_64"
+- fi
+- fi ;;
+- unknown) UNAME_PROCESSOR=powerpc ;;
+- esac
++ eval $set_cc_for_build
++ if test "$UNAME_PROCESSOR" = unknown ; then
++ UNAME_PROCESSOR=powerpc
++ fi
++ if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
++ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
++ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
++ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
++ grep IS_64BIT_ARCH >/dev/null
++ then
++ case $UNAME_PROCESSOR in
++ i386) UNAME_PROCESSOR=x86_64 ;;
++ powerpc) UNAME_PROCESSOR=powerpc64 ;;
++ esac
++ fi
++ fi
++ elif test "$UNAME_PROCESSOR" = i386 ; then
++ # Avoid executing cc on OS X 10.9, as it ships with a stub
++ # that puts up a graphical alert prompting to install
++ # developer tools. Any system running Mac OS X 10.7 or
++ # later (Darwin 11 and later) is required to have a 64-bit
++ # processor. This is not true of the ARM version of Darwin
++ # that Apple uses in portable devices.
++ UNAME_PROCESSOR=x86_64
++ fi
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+@@ -1230,7 +1294,10 @@
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+- NSE-?:NONSTOP_KERNEL:*:*)
++ NEO-?:NONSTOP_KERNEL:*:*)
++ echo neo-tandem-nsk${UNAME_RELEASE}
++ exit ;;
++ NSE-*:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+@@ -1275,13 +1342,13 @@
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+- echo mips-sei-seiux${UNAME_RELEASE}
++ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+- UNAME_MACHINE=`(uname -p) 2>/dev/null`
++ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+@@ -1299,158 +1366,10 @@
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+-esac
+-
+-#echo '(No uname command or uname output not recognized.)' 1>&2
+-#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+-
+-eval $set_cc_for_build
+-cat >$dummy.c <<EOF
+-#ifdef _SEQUENT_
+-# include <sys/types.h>
+-# include <sys/utsname.h>
+-#endif
+-main ()
+-{
+-#if defined (sony)
+-#if defined (MIPSEB)
+- /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+- I don't know.... */
+- printf ("mips-sony-bsd\n"); exit (0);
+-#else
+-#include <sys/param.h>
+- printf ("m68k-sony-newsos%s\n",
+-#ifdef NEWSOS4
+- "4"
+-#else
+- ""
+-#endif
+- ); exit (0);
+-#endif
+-#endif
+-
+-#if defined (__arm) && defined (__acorn) && defined (__unix)
+- printf ("arm-acorn-riscix\n"); exit (0);
+-#endif
+-
+-#if defined (hp300) && !defined (hpux)
+- printf ("m68k-hp-bsd\n"); exit (0);
+-#endif
+-
+-#if defined (NeXT)
+-#if !defined (__ARCHITECTURE__)
+-#define __ARCHITECTURE__ "m68k"
+-#endif
+- int version;
+- version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+- if (version < 4)
+- printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+- else
+- printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+- exit (0);
+-#endif
+-
+-#if defined (MULTIMAX) || defined (n16)
+-#if defined (UMAXV)
+- printf ("ns32k-encore-sysv\n"); exit (0);
+-#else
+-#if defined (CMU)
+- printf ("ns32k-encore-mach\n"); exit (0);
+-#else
+- printf ("ns32k-encore-bsd\n"); exit (0);
+-#endif
+-#endif
+-#endif
+-
+-#if defined (__386BSD__)
+- printf ("i386-pc-bsd\n"); exit (0);
+-#endif
+-
+-#if defined (sequent)
+-#if defined (i386)
+- printf ("i386-sequent-dynix\n"); exit (0);
+-#endif
+-#if defined (ns32000)
+- printf ("ns32k-sequent-dynix\n"); exit (0);
+-#endif
+-#endif
+-
+-#if defined (_SEQUENT_)
+- struct utsname un;
+-
+- uname(&un);
+-
+- if (strncmp(un.version, "V2", 2) == 0) {
+- printf ("i386-sequent-ptx2\n"); exit (0);
+- }
+- if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+- printf ("i386-sequent-ptx1\n"); exit (0);
+- }
+- printf ("i386-sequent-ptx\n"); exit (0);
+-
+-#endif
+-
+-#if defined (vax)
+-# if !defined (ultrix)
+-# include <sys/param.h>
+-# if defined (BSD)
+-# if BSD == 43
+- printf ("vax-dec-bsd4.3\n"); exit (0);
+-# else
+-# if BSD == 199006
+- printf ("vax-dec-bsd4.3reno\n"); exit (0);
+-# else
+- printf ("vax-dec-bsd\n"); exit (0);
+-# endif
+-# endif
+-# else
+- printf ("vax-dec-bsd\n"); exit (0);
+-# endif
+-# else
+- printf ("vax-dec-ultrix\n"); exit (0);
+-# endif
+-#endif
+-
+-#if defined (alliant) && defined (i860)
+- printf ("i860-alliant-bsd\n"); exit (0);
+-#endif
+-
+- exit (1);
+-}
+-EOF
+-
+-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+- { echo "$SYSTEM_NAME"; exit; }
+-
+-# Apollos put the system type in the environment.
+-
+-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+-
+-# Convex versions that predate uname can use getsysinfo(1)
+-
+-if [ -x /usr/convex/getsysinfo ]
+-then
+- case `getsysinfo -f cpu_type` in
+- c1*)
+- echo c1-convex-bsd
+- exit ;;
+- c2*)
+- if getsysinfo -f scalar_acc
+- then echo c32-convex-bsd
+- else echo c2-convex-bsd
+- fi
++ x86_64:VMkernel:*:*)
++ echo ${UNAME_MACHINE}-unknown-esx
+ exit ;;
+- c34*)
+- echo c34-convex-bsd
+- exit ;;
+- c38*)
+- echo c38-convex-bsd
+- exit ;;
+- c4*)
+- echo c4-convex-bsd
+- exit ;;
+- esac
+-fi
++esac
+
+ cat >&2 <<EOF
+ $0: unable to guess system type
+diff -ur librsvg-2.36.1-orig/config.sub librsvg-2.36.1/config.sub
+--- librsvg-2.36.1-orig/config.sub 2012-02-03 13:14:58.000000000 +0100
++++ librsvg-2.36.1/config.sub 2014-09-25 18:37:12.000000000 +0200
+@@ -1,38 +1,31 @@
+ #! /bin/sh
+ # Configuration validation subroutine script.
+-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+-# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+-# Free Software Foundation, Inc.
+-
+-timestamp='2009-11-20'
+-
+-# This file is (in principle) common to ALL GNU software.
+-# The presence of a machine in this file suggests that SOME GNU software
+-# can handle that machine. It does not imply ALL GNU software can.
+-#
+-# This file is free software; you can redistribute it and/or modify
+-# it under the terms of the GNU General Public License as published by
+-# the Free Software Foundation; either version 2 of the License, or
++# Copyright 1992-2014 Free Software Foundation, Inc.
++
++timestamp='2014-09-11'
++
++# This file is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 3 of the License, or
+ # (at your option) any later version.
+ #
+-# This program is distributed in the hope that it will be useful,
+-# but WITHOUT ANY WARRANTY; without even the implied warranty of
+-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-# GNU General Public License for more details.
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++# General Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License
+-# along with this program; if not, write to the Free Software
+-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+-# 02110-1301, USA.
++# along with this program; if not, see <http://www.gnu.org/licenses/>.
+ #
+ # As a special exception to the GNU General Public License, if you
+ # distribute this file as part of a program that contains a
+ # configuration script generated by Autoconf, you may include it under
+-# the same distribution terms that you use for the rest of that program.
++# the same distribution terms that you use for the rest of that
++# program. This Exception is an additional permission under section 7
++# of the GNU General Public License, version 3 ("GPLv3").
+
+
+-# Please send patches to <config-patches@gnu.org>. Submit a context
+-# diff and a properly formatted GNU ChangeLog entry.
++# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+ #
+ # Configuration subroutine to validate and canonicalize a configuration type.
+ # Supply the specified configuration type as an argument.
+@@ -75,8 +68,7 @@
+ version="\
+ GNU config.sub ($timestamp)
+
+-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+-2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
++Copyright 1992-2014 Free Software Foundation, Inc.
+
+ This is free software; see the source for copying conditions. There is NO
+ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+@@ -123,13 +115,18 @@
+ # Here we must recognize all the valid KERNEL-OS combinations.
+ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+ case $maybe_os in
+- nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
+- uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
++ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
++ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
++ knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
++ android-linux)
++ os=-linux-android
++ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
++ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+@@ -152,12 +149,12 @@
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+- -apple | -axis | -knuth | -cray | -microblaze)
++ -apple | -axis | -knuth | -cray | -microblaze*)
+ os=
+ basic_machine=$1
+ ;;
+- -bluegene*)
+- os=-cnk
++ -bluegene*)
++ os=-cnk
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+@@ -173,10 +170,10 @@
+ os=-chorusos
+ basic_machine=$1
+ ;;
+- -chorusrdb)
+- os=-chorusrdb
++ -chorusrdb)
++ os=-chorusrdb
+ basic_machine=$1
+- ;;
++ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+@@ -221,6 +218,12 @@
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
++ -lynx*178)
++ os=-lynxos178
++ ;;
++ -lynx*5)
++ os=-lynxos5
++ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+@@ -245,20 +248,28 @@
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
++ | aarch64 | aarch64_be \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+- | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
++ | arc | arceb \
++ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
++ | avr | avr32 \
++ | be32 | be64 \
+ | bfin \
+- | c4x | clipper \
++ | c4x | c8051 | clipper \
+ | d10v | d30v | dlx | dsp16xx \
++ | epiphany \
+ | fido | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
++ | hexagon \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
++ | k1om \
++ | le32 | le64 \
+ | lm32 \
+ | m32c | m32r | m32rle | m68000 | m68k | m88k \
+- | maxq | mb | microblaze | mcore | mep | metag \
++ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+@@ -272,38 +283,51 @@
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
++ | mipsisa32r6 | mipsisa32r6el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
++ | mipsisa64r6 | mipsisa64r6el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
++ | mipsr5900 | mipsr5900el \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+- | nios | nios2 \
++ | nds32 | nds32le | nds32be \
++ | nios | nios2 | nios2eb | nios2el \
+ | ns16k | ns32k \
+- | or32 \
++ | open8 | or1k | or1knd | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+- | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
++ | powerpc | powerpc64 | powerpc64le | powerpcle \
+ | pyramid \
+- | rx \
++ | riscv32 | riscv64 \
++ | rl78 | rx \
+ | score \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+- | spu | strongarm \
+- | tahoe | thumb | tic4x | tic80 | tron \
++ | spu \
++ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+ | ubicom32 \
+- | v850 | v850e \
++ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+ | we32k \
+- | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
++ | x86 | xc16x | xstormy16 | xtensa \
+ | z8k | z80)
+ basic_machine=$basic_machine-unknown
+ ;;
+- m6811 | m68hc11 | m6812 | m68hc12 | picochip)
+- # Motorola 68HC11/12.
++ c54x)
++ basic_machine=tic54x-unknown
++ ;;
++ c55x)
++ basic_machine=tic55x-unknown
++ ;;
++ c6x)
++ basic_machine=tic6x-unknown
++ ;;
++ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+@@ -313,6 +337,21 @@
+ basic_machine=mt-unknown
+ ;;
+
++ strongarm | thumb | xscale)
++ basic_machine=arm-unknown
++ ;;
++ xgate)
++ basic_machine=$basic_machine-unknown
++ os=-none
++ ;;
++ xscaleeb)
++ basic_machine=armeb-unknown
++ ;;
++
++ xscaleel)
++ basic_machine=armel-unknown
++ ;;
++
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+@@ -327,25 +366,31 @@
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
++ | aarch64-* | aarch64_be-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+- | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
++ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* | avr32-* \
++ | be32-* | be64-* \
+ | bfin-* | bs2000-* \
+- | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+- | clipper-* | craynv-* | cydra-* \
++ | c[123]* | c30-* | [cjt]90-* | c4x-* \
++ | c8051-* | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
++ | hexagon-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
++ | k1om-* \
++ | le32-* | le64-* \
+ | lm32-* \
+ | m32c-* | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+- | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
++ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
++ | microblaze-* | microblazeel-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+@@ -359,33 +404,41 @@
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
++ | mipsisa32r6-* | mipsisa32r6el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
++ | mipsisa64r6-* | mipsisa64r6el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
++ | mipsr5900-* | mipsr5900el-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | mt-* \
+ | msp430-* \
+- | nios-* | nios2-* \
++ | nds32-* | nds32le-* | nds32be-* \
++ | nios-* | nios2-* | nios2eb-* | nios2el-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
++ | open8-* \
++ | or1k*-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
++ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+ | pyramid-* \
+- | romp-* | rs6000-* | rx-* \
++ | rl78-* | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+- | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
+- | tahoe-* | thumb-* \
+- | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \
++ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
++ | tahoe-* \
++ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
++ | tile*-* \
+ | tron-* \
+ | ubicom32-* \
+- | v850-* | v850e-* | vax-* \
++ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
++ | vax-* \
+ | we32k-* \
+- | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
++ | x86-* | x86_64-* | xc16x-* | xps100-* \
+ | xstormy16-* | xtensa*-* \
+ | ymp-* \
+ | z8k-* | z80-*)
+@@ -410,7 +463,7 @@
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+- abacus)
++ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+@@ -480,11 +533,20 @@
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
++ c54x-*)
++ basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
++ ;;
++ c55x-*)
++ basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
++ ;;
++ c6x-*)
++ basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
++ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+- cegcc)
++ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
+@@ -516,7 +578,7 @@
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+- cr16)
++ cr16 | cr16-*)
+ basic_machine=cr16-unknown
+ os=-elf
+ ;;
+@@ -674,7 +736,6 @@
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+-# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+@@ -732,11 +793,15 @@
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+- microblaze)
++ microblaze*)
+ basic_machine=microblaze-xilinx
+ ;;
++ mingw64)
++ basic_machine=x86_64-pc
++ os=-mingw64
++ ;;
+ mingw32)
+- basic_machine=i386-pc
++ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ mingw32ce)
+@@ -764,6 +829,10 @@
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
++ moxiebox)
++ basic_machine=moxie-unknown
++ os=-moxiebox
++ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+@@ -771,10 +840,18 @@
+ ms1-*)
+ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ ;;
++ msys)
++ basic_machine=i686-pc
++ os=-msys
++ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
++ nacl)
++ basic_machine=le32-unknown
++ os=-nacl
++ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+@@ -839,6 +916,12 @@
+ np1)
+ basic_machine=np1-gould
+ ;;
++ neo-tandem)
++ basic_machine=neo-tandem
++ ;;
++ nse-tandem)
++ basic_machine=nse-tandem
++ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+@@ -921,9 +1004,10 @@
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+- ppc) basic_machine=powerpc-unknown
++ ppc | ppcbe) basic_machine=powerpc-unknown
+ ;;
+- ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
++ ppc-* | ppcbe-*)
++ basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+@@ -948,7 +1032,11 @@
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+- rdos)
++ rdos | rdos64)
++ basic_machine=x86_64-pc
++ os=-rdos
++ ;;
++ rdos32)
+ basic_machine=i386-pc
+ os=-rdos
+ ;;
+@@ -1017,6 +1105,9 @@
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
++ strongarm-* | thumb-*)
++ basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
++ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+@@ -1073,20 +1164,8 @@
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+- tic54x | c54x*)
+- basic_machine=tic54x-unknown
+- os=-coff
+- ;;
+- tic55x | c55x*)
+- basic_machine=tic55x-unknown
+- os=-coff
+- ;;
+- tic6x | c6x*)
+- basic_machine=tic6x-unknown
+- os=-coff
+- ;;
+ tile*)
+- basic_machine=tile-unknown
++ basic_machine=$basic_machine-unknown
+ os=-linux-gnu
+ ;;
+ tx39)
+@@ -1156,6 +1235,9 @@
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
++ xscale-* | xscalee[bl]-*)
++ basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
++ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+@@ -1253,11 +1335,11 @@
+ if [ x"$os" != x"" ]
+ then
+ case $os in
+- # First match some system type aliases
+- # that might get confused with valid system types.
++ # First match some system type aliases
++ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+- -auroraux)
+- os=-auroraux
++ -auroraux)
++ os=-auroraux
+ ;;
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+@@ -1281,28 +1363,29 @@
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+- | -sym* | -kopensolaris* \
++ | -sym* | -kopensolaris* | -plan9* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* | -aros* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+- | -openbsd* | -solidbsd* \
++ | -bitrig* | -openbsd* | -solidbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* | -cegcc* \
+- | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+- | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+- | -uxpv* | -beos* | -mpeix* | -udk* \
++ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
++ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
++ | -linux-newlib* | -linux-musl* | -linux-uclibc* \
++ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+- | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
++ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+@@ -1341,7 +1424,7 @@
+ -opened*)
+ os=-openedition
+ ;;
+- -os400*)
++ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+@@ -1390,7 +1473,7 @@
+ -sinix*)
+ os=-sysv4
+ ;;
+- -tpf*)
++ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+@@ -1426,15 +1509,14 @@
+ -aros*)
+ os=-aros
+ ;;
+- -kaos*)
+- os=-kaos
+- ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -dicos*)
+ os=-dicos
+ ;;
++ -nacl*)
++ ;;
+ -none)
+ ;;
+ *)
+@@ -1457,10 +1539,10 @@
+ # system, and we'll never get to this point.
+
+ case $basic_machine in
+- score-*)
++ score-*)
+ os=-elf
+ ;;
+- spu-*)
++ spu-*)
+ os=-elf
+ ;;
+ *-acorn)
+@@ -1472,8 +1554,23 @@
+ arm*-semi)
+ os=-aout
+ ;;
+- c4x-* | tic4x-*)
+- os=-coff
++ c4x-* | tic4x-*)
++ os=-coff
++ ;;
++ c8051-*)
++ os=-elf
++ ;;
++ hexagon-*)
++ os=-elf
++ ;;
++ tic54x-*)
++ os=-coff
++ ;;
++ tic55x-*)
++ os=-coff
++ ;;
++ tic6x-*)
++ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+@@ -1493,14 +1590,11 @@
+ ;;
+ m68000-sun)
+ os=-sunos3
+- # This also exists in the configure program, but was not the
+- # default.
+- # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+- mep-*)
++ mep-*)
+ os=-elf
+ ;;
+ mips*-cisco)
+@@ -1527,7 +1621,7 @@
+ *-ibm)
+ os=-aix
+ ;;
+- *-knuth)
++ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
diff --git a/Tools/gtk/patches/llvm-elf-add-stackmaps-arm64.patch b/Tools/gtk/patches/llvm-elf-add-stackmaps-arm64.patch
new file mode 100644
index 000000000..caa8c1f1b
--- /dev/null
+++ b/Tools/gtk/patches/llvm-elf-add-stackmaps-arm64.patch
@@ -0,0 +1,13 @@
+diff --git a/lib/Target/AArch64/AArch64AsmPrinter.cpp b/lib/Target/AArch64/AArch64AsmPrinter.cpp
+index 8bee4f5..bfbbaac 100644
+--- a/lib/Target/AArch64/AArch64AsmPrinter.cpp
++++ b/lib/Target/AArch64/AArch64AsmPrinter.cpp
+@@ -154,6 +154,8 @@ void AArch64AsmPrinter::EmitEndOfAsmFile(Module &M) {
+ }
+ Stubs.clear();
+ }
++
++ SM.serializeToStackMapSection();
+ }
+
+ }
diff --git a/Tools/gtk/patches/llvm-elf-add-stackmaps.patch b/Tools/gtk/patches/llvm-elf-add-stackmaps.patch
new file mode 100644
index 000000000..877214d97
--- /dev/null
+++ b/Tools/gtk/patches/llvm-elf-add-stackmaps.patch
@@ -0,0 +1,46 @@
+commit ec9de4677ac61f2164d7c80cae5da0008189efa3
+Author: Philip Reames <listmail@philipreames.com>
+Date: Fri Aug 1 18:47:09 2014 +0000
+
+ Add support for StackMap section for ELF/Linux systems
+
+ This patch adds code to emits the StackMap section on ELF systems. This section is required to support llvm.experimental.stackmap and llvm.experimental.patchpoint intrinsics.
+
+ Reviewers: ributzka, echristo
+
+ Differential Revision: http://reviews.llvm.org/D4574
+
+
+
+ git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@214538 91177308-0d34-0410-b5e6-96231b3b80d8
+
+diff --git a/lib/MC/MCObjectFileInfo.cpp b/lib/MC/MCObjectFileInfo.cpp
+index df5787c..da707d8 100644
+--- a/lib/MC/MCObjectFileInfo.cpp
++++ b/lib/MC/MCObjectFileInfo.cpp
+@@ -583,6 +583,12 @@ void MCObjectFileInfo::InitELFMCObjectFileInfo(Triple T) {
+ DwarfAddrSection =
+ Ctx->getELFSection(".debug_addr", ELF::SHT_PROGBITS, 0,
+ SectionKind::getMetadata());
++
++ StackMapSection =
++ Ctx->getELFSection(".llvm_stackmaps", ELF::SHT_PROGBITS,
++ ELF::SHF_ALLOC,
++ SectionKind::getMetadata());
++
+ }
+
+
+diff --git a/lib/Target/X86/X86AsmPrinter.cpp b/lib/Target/X86/X86AsmPrinter.cpp
+index 3c22e88..fc0d408 100644
+--- a/lib/Target/X86/X86AsmPrinter.cpp
++++ b/lib/Target/X86/X86AsmPrinter.cpp
+@@ -736,6 +736,8 @@ void X86AsmPrinter::EmitEndOfAsmFile(Module &M) {
+ }
+ Stubs.clear();
+ }
++
++ SM.serializeToStackMapSection();
+ }
+ }
+
diff --git a/Tools/gtk/patches/llvm-elf-allow-fde-references-outside-the-2gb-range-arm64.patch b/Tools/gtk/patches/llvm-elf-allow-fde-references-outside-the-2gb-range-arm64.patch
new file mode 100644
index 000000000..be4412adf
--- /dev/null
+++ b/Tools/gtk/patches/llvm-elf-allow-fde-references-outside-the-2gb-range-arm64.patch
@@ -0,0 +1,23 @@
+
+diff --git a/lib/MC/MCObjectFileInfo.cpp b/lib/MC/MCObjectFileInfo.cpp
+index 7886ab6..96929e5 100644
+--- a/lib/MC/MCObjectFileInfo.cpp
++++ b/lib/MC/MCObjectFileInfo.cpp
+@@ -284,6 +284,17 @@ void MCObjectFileInfo::InitELFMCObjectFileInfo(Triple T) {
+ ? dwarf::DW_EH_PE_udata4 : dwarf::DW_EH_PE_absptr;
+ }
+ break;
++ case Triple::aarch64:
++ if (RelocM == Reloc::PIC_) {
++ FDECFIEncoding = dwarf::DW_EH_PE_pcrel |
++ ((CMModel == CodeModel::Small || CMModel == CodeModel::Medium)
++ ? dwarf::DW_EH_PE_sdata4 : dwarf::DW_EH_PE_sdata8);
++ } else {
++ FDECFIEncoding =
++ (CMModel == CodeModel::Small || CMModel == CodeModel::Medium)
++ ? dwarf::DW_EH_PE_udata4 : dwarf::DW_EH_PE_absptr;
++ }
++ break;
+ default:
+ FDECFIEncoding = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4;
+ break;
diff --git a/Tools/gtk/patches/llvm-elf-allow-fde-references-outside-the-2gb-range.patch b/Tools/gtk/patches/llvm-elf-allow-fde-references-outside-the-2gb-range.patch
new file mode 100644
index 000000000..63004eb1f
--- /dev/null
+++ b/Tools/gtk/patches/llvm-elf-allow-fde-references-outside-the-2gb-range.patch
@@ -0,0 +1,281 @@
+commit 21bcdeb1d65b4be0d716693f3dcabd2e8a7c6386
+Author: Joerg Sonnenberger <joerg@bec.de>
+Date: Tue Nov 25 13:37:55 2014 +0000
+
+ Reapply 222538 and update tests to explicitly request small code model
+ and PIC:
+
+ Allow FDE references outside the +/-2GB range supported by PC relative
+ offsets for code models other than small/medium. For JIT application,
+ memory layout is less controlled and can result in truncations
+ otherwise.
+
+ Patch from Akos Kiss.
+
+ Differential Revision: http://reviews.llvm.org/D6079
+
+
+ git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@222760 91177308-0d34-0410-b5e6-96231b3b80d8
+
+diff --git a/lib/MC/MCObjectFileInfo.cpp b/lib/MC/MCObjectFileInfo.cpp
+index 7886ab6..96929e5 100644
+--- a/lib/MC/MCObjectFileInfo.cpp
++++ b/lib/MC/MCObjectFileInfo.cpp
+@@ -273,6 +273,17 @@ void MCObjectFileInfo::InitELFMCObjectFileInfo(Triple T) {
+ case Triple::mips64el:
+ FDECFIEncoding = dwarf::DW_EH_PE_sdata8;
+ break;
++ case Triple::x86_64:
++ if (RelocM == Reloc::PIC_) {
++ FDECFIEncoding = dwarf::DW_EH_PE_pcrel |
++ ((CMModel == CodeModel::Small || CMModel == CodeModel::Medium)
++ ? dwarf::DW_EH_PE_sdata4 : dwarf::DW_EH_PE_sdata8);
++ } else {
++ FDECFIEncoding =
++ (CMModel == CodeModel::Small || CMModel == CodeModel::Medium)
++ ? dwarf::DW_EH_PE_udata4 : dwarf::DW_EH_PE_absptr;
++ }
++ break;
+ default:
+ FDECFIEncoding = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4;
+ break;
+diff --git a/test/MC/ELF/cfi-adjust-cfa-offset.s b/test/MC/ELF/cfi-adjust-cfa-offset.s
+index 9d639f7..f31a6b0 100644
+--- a/test/MC/ELF/cfi-adjust-cfa-offset.s
++++ b/test/MC/ELF/cfi-adjust-cfa-offset.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-advance-loc2.s b/test/MC/ELF/cfi-advance-loc2.s
+index 98caa018..c11ccaf 100644
+--- a/test/MC/ELF/cfi-advance-loc2.s
++++ b/test/MC/ELF/cfi-advance-loc2.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ // test that this produces a correctly encoded cfi_advance_loc2
+
+diff --git a/test/MC/ELF/cfi-def-cfa-offset.s b/test/MC/ELF/cfi-def-cfa-offset.s
+index 59f7400..93158ce 100644
+--- a/test/MC/ELF/cfi-def-cfa-offset.s
++++ b/test/MC/ELF/cfi-def-cfa-offset.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-def-cfa-register.s b/test/MC/ELF/cfi-def-cfa-register.s
+index 178ba32..8c55053 100644
+--- a/test/MC/ELF/cfi-def-cfa-register.s
++++ b/test/MC/ELF/cfi-def-cfa-register.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-def-cfa.s b/test/MC/ELF/cfi-def-cfa.s
+index dfb0d4b..f0b4934 100644
+--- a/test/MC/ELF/cfi-def-cfa.s
++++ b/test/MC/ELF/cfi-def-cfa.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-escape.s b/test/MC/ELF/cfi-escape.s
+index 5394ee4..3b76746 100644
+--- a/test/MC/ELF/cfi-escape.s
++++ b/test/MC/ELF/cfi-escape.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-offset.s b/test/MC/ELF/cfi-offset.s
+index a65b4fc..02f31ba 100644
+--- a/test/MC/ELF/cfi-offset.s
++++ b/test/MC/ELF/cfi-offset.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-register.s b/test/MC/ELF/cfi-register.s
+index 9441770..3433bef 100644
+--- a/test/MC/ELF/cfi-register.s
++++ b/test/MC/ELF/cfi-register.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-rel-offset.s b/test/MC/ELF/cfi-rel-offset.s
+index 0dc69c8..f51b2d3 100644
+--- a/test/MC/ELF/cfi-rel-offset.s
++++ b/test/MC/ELF/cfi-rel-offset.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-rel-offset2.s b/test/MC/ELF/cfi-rel-offset2.s
+index 360e7b0..0ce8d03 100644
+--- a/test/MC/ELF/cfi-rel-offset2.s
++++ b/test/MC/ELF/cfi-rel-offset2.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-remember.s b/test/MC/ELF/cfi-remember.s
+index 3a38948..c98695a 100644
+--- a/test/MC/ELF/cfi-remember.s
++++ b/test/MC/ELF/cfi-remember.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-restore.s b/test/MC/ELF/cfi-restore.s
+index e225797..99a74e7 100644
+--- a/test/MC/ELF/cfi-restore.s
++++ b/test/MC/ELF/cfi-restore.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-same-value.s b/test/MC/ELF/cfi-same-value.s
+index 2d37f4d..b7329d1 100644
+--- a/test/MC/ELF/cfi-same-value.s
++++ b/test/MC/ELF/cfi-same-value.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-signal-frame.s b/test/MC/ELF/cfi-signal-frame.s
+index 98deb0a..9558d7b 100644
+--- a/test/MC/ELF/cfi-signal-frame.s
++++ b/test/MC/ELF/cfi-signal-frame.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-undefined.s b/test/MC/ELF/cfi-undefined.s
+index 568b315..09000c9 100644
+--- a/test/MC/ELF/cfi-undefined.s
++++ b/test/MC/ELF/cfi-undefined.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f:
+ .cfi_startproc
+diff --git a/test/MC/ELF/cfi-window-save.s b/test/MC/ELF/cfi-window-save.s
+index b083901..dd20164 100644
+--- a/test/MC/ELF/cfi-window-save.s
++++ b/test/MC/ELF/cfi-window-save.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ # Should use SPARC as the target to test this. However, SPARC does not
+ # use MC yet.
+diff --git a/test/MC/ELF/cfi-zero-addr-delta.s b/test/MC/ELF/cfi-zero-addr-delta.s
+index 8662839..61118ec 100644
+--- a/test/MC/ELF/cfi-zero-addr-delta.s
++++ b/test/MC/ELF/cfi-zero-addr-delta.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ // Test that we don't produce a DW_CFA_advance_loc 0
+
+diff --git a/test/MC/ELF/cfi.s b/test/MC/ELF/cfi.s
+index 21be615..42b0189 100644
+--- a/test/MC/ELF/cfi.s
++++ b/test/MC/ELF/cfi.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -sr -sd | FileCheck %s
++// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -s -sr -sd | FileCheck %s
+
+ f1:
+ .cfi_startproc
+diff --git a/test/MC/ELF/pr19430.s b/test/MC/ELF/pr19430.s
+index a1e5246..528193d 100644
+--- a/test/MC/ELF/pr19430.s
++++ b/test/MC/ELF/pr19430.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -triple x86_64-pc-linux-gnu %s -filetype=obj -o - | llvm-readobj -r | FileCheck %s
++// RUN: llvm-mc -triple x86_64-pc-linux-gnu %s -filetype=obj -o - \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-readobj -r | FileCheck %s
+
+ // Test that we can use .cfi_startproc without a global symbol.
+
+diff --git a/test/MC/X86/fde-reloc.s b/test/MC/X86/fde-reloc.s
+index 63ac976..9b5de15 100644
+--- a/test/MC/X86/fde-reloc.s
++++ b/test/MC/X86/fde-reloc.s
+@@ -1,4 +1,6 @@
+-// RUN: llvm-mc -filetype=obj %s -o - -triple x86_64-pc-linux | llvm-objdump -r - | FileCheck --check-prefix=X86-64 %s
++// RUN: llvm-mc -filetype=obj %s -o - -triple x86_64-pc-linux \
++// RUN: -relocation-model=pic -code-model=small \
++// RUN: | llvm-objdump -r - | FileCheck --check-prefix=X86-64 %s
+ // RUN: llvm-mc -filetype=obj %s -o - -triple i686-pc-linux | llvm-objdump -r - | FileCheck --check-prefix=I686 %s
+
+ // PR15448
diff --git a/Tools/gtk/patches/mesa-gallivm-Fix-build-after-LLVM-commit-211259.patch b/Tools/gtk/patches/mesa-gallivm-Fix-build-after-LLVM-commit-211259.patch
new file mode 100644
index 000000000..75f38e552
--- /dev/null
+++ b/Tools/gtk/patches/mesa-gallivm-Fix-build-after-LLVM-commit-211259.patch
@@ -0,0 +1,29 @@
+From 564821c917f4a9d5a0de2ee77b90b0cd85e3d3a6 Mon Sep 17 00:00:00 2001
+From: Aaron Watry <awatry@gmail.com>
+Date: Fri, 20 Jun 2014 19:13:30 -0500
+Subject: [PATCH] gallivm: Fix build after LLVM commit 211259
+
+Signed-off-by: Aaron Watry <awatry@gmail.com>
+Reviewed-by: Tom Stellard <thomas.stellard@amd.com>
+---
+ src/gallium/auxiliary/gallivm/lp_bld_debug.cpp | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/gallium/auxiliary/gallivm/lp_bld_debug.cpp b/src/gallium/auxiliary/gallivm/lp_bld_debug.cpp
+index df26883..413a0c2 100644
+--- a/src/gallium/auxiliary/gallivm/lp_bld_debug.cpp
++++ b/src/gallium/auxiliary/gallivm/lp_bld_debug.cpp
+@@ -51,7 +51,9 @@
+ #include <llvm/MC/MCInstPrinter.h>
+ #include <llvm/MC/MCRegisterInfo.h>
+
+-#if HAVE_LLVM >= 0x0303
++#if HAVE_LLVM >= 0x0305
++#define OwningPtr std::unique_ptr
++#elif HAVE_LLVM >= 0x0303
+ #include <llvm/ADT/OwningPtr.h>
+ #endif
+
+--
+2.1.0
+
diff --git a/Tools/gtk/patches/rtspsrc-timeout-on-udpsrc-is-in-nanoseconds.patch b/Tools/gtk/patches/rtspsrc-timeout-on-udpsrc-is-in-nanoseconds.patch
new file mode 100644
index 000000000..87ed49087
--- /dev/null
+++ b/Tools/gtk/patches/rtspsrc-timeout-on-udpsrc-is-in-nanoseconds.patch
@@ -0,0 +1,27 @@
+From e0bb1fe30985aa0784825388500cc4fa92c1ff9c Mon Sep 17 00:00:00 2001
+From: Wim Taymans <wim.taymans@collabora.co.uk>
+Date: Wed, 12 Dec 2012 11:09:42 +0100
+Subject: [PATCH 2/2] rtspsrc: timeout on udpsrc is in nanoseconds
+
+---
+ gst/rtsp/gstrtspsrc.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c
+index 98577b8..902d8ff 100644
+--- a/gst/rtsp/gstrtspsrc.c
++++ b/gst/rtsp/gstrtspsrc.c
+@@ -2812,8 +2812,8 @@ gst_rtspsrc_stream_configure_udp (GstRTSPSrc * src, GstRTSPStream * stream,
+ /* configure a timeout on the UDP port. When the timeout message is
+ * posted, we assume UDP transport is not possible. We reconnect using TCP
+ * if we can. */
+- g_object_set (G_OBJECT (stream->udpsrc[0]), "timeout", src->udp_timeout,
+- NULL);
++ g_object_set (G_OBJECT (stream->udpsrc[0]), "timeout",
++ src->udp_timeout * 1000, NULL);
+
+ /* get output pad of the UDP source. */
+ *outpad = gst_element_get_static_pad (stream->udpsrc[0], "src");
+--
+1.8.1.2
+
diff --git a/Tools/gtk/patches/udpsrc-improve-timeouts.patch b/Tools/gtk/patches/udpsrc-improve-timeouts.patch
new file mode 100644
index 000000000..d26e346a8
--- /dev/null
+++ b/Tools/gtk/patches/udpsrc-improve-timeouts.patch
@@ -0,0 +1,53 @@
+From dbbdf54778771535dfea5ddbdeeaba89d9bc7be6 Mon Sep 17 00:00:00 2001
+From: Wim Taymans <wim.taymans@collabora.co.uk>
+Date: Wed, 12 Dec 2012 11:08:13 +0100
+Subject: [PATCH 1/2] udpsrc: improve timeouts
+
+Make it possible to set the timeout after we went to the READY state by using
+the timeout when checking the condition. This also makes it possible to set the
+timeout with a higher granularity than seconds.
+---
+ gst/udp/gstudpsrc.c | 16 ++++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+diff --git a/gst/udp/gstudpsrc.c b/gst/udp/gstudpsrc.c
+index bdad5b3..5b54021 100644
+--- a/gst/udp/gstudpsrc.c
++++ b/gst/udp/gstudpsrc.c
+@@ -397,13 +397,20 @@ retry:
+ goto no_select;
+
+ do {
++ gint64 timeout;
++
+ try_again = FALSE;
+
++ if (udpsrc->timeout)
++ timeout = udpsrc->timeout / 1000;
++ else
++ timeout = -1;
++
+ GST_LOG_OBJECT (udpsrc, "doing select, timeout %" G_GUINT64_FORMAT,
+- udpsrc->timeout);
++ timeout);
+
+- if (!g_socket_condition_wait (udpsrc->used_socket, G_IO_IN | G_IO_PRI,
+- udpsrc->cancellable, &err)) {
++ if (!g_socket_condition_timed_wait (udpsrc->used_socket, G_IO_IN | G_IO_PRI,
++ timeout, udpsrc->cancellable, &err)) {
+ if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_BUSY)
+ || g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ goto stopped;
+@@ -823,9 +830,6 @@ gst_udpsrc_start (GstBaseSrc * bsrc)
+ goto getsockname_error;
+ }
+
+- if (src->timeout)
+- g_socket_set_timeout (src->used_socket, src->timeout / GST_SECOND);
+-
+ #if GLIB_CHECK_VERSION (2, 35, 7)
+ {
+ gint val = 0;
+--
+1.8.1.2
+
diff --git a/Tools/gtk/patches/xserver-fix-glx-init.patch b/Tools/gtk/patches/xserver-fix-glx-init.patch
new file mode 100644
index 000000000..2dc965bbe
--- /dev/null
+++ b/Tools/gtk/patches/xserver-fix-glx-init.patch
@@ -0,0 +1,14 @@
+diff --git a/mi/miinitext.c b/mi/miinitext.c
+index 6ceae05..cda4e43 100644
+--- a/mi/miinitext.c
++++ b/mi/miinitext.c
+@@ -540,6 +540,9 @@ static ExtensionModule staticExtensions[] = {
+ #ifdef DAMAGE
+ {DamageExtensionInit, "DAMAGE", &noDamageExtension, NULL},
+ #endif
++#ifdef GLXEXT
++ {GlxExtensionInit, "GLX", &noGlxExtension, NULL},
++#endif
+ {NULL, NULL, NULL, NULL, NULL}
+ };
+
diff --git a/Tools/gtk/patches/xserver-remove-bogus-dependencies.patch b/Tools/gtk/patches/xserver-remove-bogus-dependencies.patch
new file mode 100644
index 000000000..97b1a1314
--- /dev/null
+++ b/Tools/gtk/patches/xserver-remove-bogus-dependencies.patch
@@ -0,0 +1,43 @@
+From 879f42531ff04be578c39f9d44548aeb3ded67fd Mon Sep 17 00:00:00 2001
+From: Gustavo Noronha Silva <gustavo.noronha@collabora.com>
+Date: Wed, 8 May 2013 19:44:15 -0300
+Subject: [PATCH 2/2] Filter out -l parameters when setting dependencies
+
+Newer make (Fedora 19) gets confused when it finds a -l parameter in a
+dependency, and tries to make it as a target, causing the build to fail.
+
+Signed-off-by: Gustavo Noronha Silva <gustavo.noronha@collabora.com>
+---
+ hw/vfb/Makefile.am | 2 +-
+ test/Makefile.am | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/hw/vfb/Makefile.am b/hw/vfb/Makefile.am
+index 9f4992c..06830ae 100644
+--- a/hw/vfb/Makefile.am
++++ b/hw/vfb/Makefile.am
+@@ -25,7 +25,7 @@ XVFB_LIBS = \
+ $(XSERVER_LIBS)
+
+ Xvfb_LDADD = $(XVFB_LIBS) $(XVFB_SYS_LIBS) $(XSERVER_SYS_LIBS)
+-Xvfb_DEPENDENCIES = $(XVFB_LIBS)
++Xvfb_DEPENDENCIES = $(filter-out -l%,$(XVFB_LIBS))
+ Xvfb_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG)
+
+ relink:
+diff --git a/test/Makefile.am b/test/Makefile.am
+index 34f53fc..3509306 100644
+--- a/test/Makefile.am
++++ b/test/Makefile.am
+@@ -117,7 +117,7 @@ libxservertest_la_LIBADD += \
+ endif
+ endif
+
+-libxservertest_la_DEPENDENCIES = $(libxservertest_la_LIBADD)
++libxservertest_la_DEPENDENCIES = $(filter-out -l%,$(libxservertest_la_LIBADD))
+ endif
+
+ EXTRA_DIST = ddxstubs.c
+--
+1.8.2.1
+
diff --git a/Tools/gtk/webkitdom.py b/Tools/gtk/webkitdom.py
new file mode 100755
index 000000000..e1bc7950c
--- /dev/null
+++ b/Tools/gtk/webkitdom.py
@@ -0,0 +1,261 @@
+#!/usr/bin/env python
+# Copyright (C) 2013 Igalia S.L.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+import common
+import errno
+import os
+import re
+import sys
+
+from ConfigParser import SafeConfigParser
+
+
+class WebKitDOMDocGenerator(object):
+
+ def __init__(self, symbol_files, file_handle):
+ self._symbol_files = symbol_files
+ self._file_handle = file_handle
+
+ def write_header(self):
+ pass
+
+ def write_section(self, symbol_file):
+ raise NotImplementedError
+
+ def write_footer(self):
+ pass
+
+ def write(self, string):
+ self._file_handle.write(string)
+
+ @staticmethod
+ def is_deprecated_symbol_file(file_path):
+ return 'WebKitDOMDeprecated' in file_path
+
+ def generate(self):
+ self.write_header()
+ for symbol_file in self._symbol_files:
+ if WebKitDOMDocGenerator.is_deprecated_symbol_file(symbol_file):
+ continue
+ self.write_section(symbol_file)
+ self.write_footer()
+
+
+class WebKitDOMDocGeneratorSGML(WebKitDOMDocGenerator):
+ def write_header(self):
+ self.write('''<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>WebKitDOMGTK+ Reference Manual</title>
+ <releaseinfo>for WebKitDOMGTK+ &version;</releaseinfo>
+ </bookinfo>
+
+ <chapter>
+ <title>Class Overview</title>
+''')
+
+ def write_section(self, symbol_file):
+ basename = os.path.basename(symbol_file)
+ self.write(' <xi:include href="xml/%s"/>\n' % basename.replace(".symbols", ".xml"))
+
+ def write_footer(self):
+ self.write(''' </chapter>
+
+ <index id="index-all">
+ <title>Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+ <index id="api-index-deprecated" role="deprecated">
+ <title>Index of deprecated symbols</title>
+ <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+ </index>
+
+ <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+</book>
+''')
+
+
+class WebKitDOMDocGeneratorSections(WebKitDOMDocGenerator):
+ def __init__(self, symbol_files, file_handle):
+ super(WebKitDOMDocGeneratorSections, self).__init__(symbol_files, file_handle)
+
+ self._first_decamelize_re = re.compile('(.)([A-Z][a-z]+)')
+ self._second_decamelize_re = re.compile('([a-z0-9])([A-Z])')
+ self._dom_class_re = re.compile('(^WebKitDOM)(.+)$')
+ self._function_re = re.compile('^.+ (.+)\((.+)\)$')
+ self._constant_re = re.compile('^[A-Z_]+$')
+
+ self.deprecated_symbols = {}
+ for symbol_file in symbol_files:
+ if WebKitDOMDocGenerator.is_deprecated_symbol_file(symbol_file):
+ self._deprecated_symbols = self._find_deprecated_symbols(symbol_file)
+
+ def _dom_class(self, class_name):
+ return self._dom_class_re.sub(r'\2', class_name)
+
+ def _dom_class_decamelize(self, class_name):
+ s1 = self._first_decamelize_re.sub(r'\1_\2', self._dom_class(class_name))
+ retval = self._second_decamelize_re.sub(r'\1_\2', s1)
+
+ # Fix some exceptions.
+ retval = retval.replace('Web_Kit', 'WebKit')
+ retval = retval.replace('X_Path', 'XPath')
+ retval = retval.replace('HTMLI_Frame', 'HTML_IFrame')
+ retval = retval.replace('HTMLBR', 'HTML_BR')
+ retval = retval.replace('HTMLHR', 'HTML_HR')
+ retval = retval.replace('HTMLLI', 'HTML_LI')
+ retval = retval.replace('HTMLD', 'HTML_D')
+ retval = retval.replace('HTMLO', 'HTML_O')
+ retval = retval.replace('HTMLU', 'HTML_U')
+
+ return retval
+
+ def _find_deprecated_symbols(self, symbol_file):
+ retval = {}
+ f = open(symbol_file, 'r')
+ for line in f.readlines():
+ match = self._function_re.match(line)
+ if not match:
+ continue
+
+ function = match.group(1)
+ args = match.group(2).split(', ')
+ class_name = args[0].strip('*')
+ retval.setdefault(class_name, []).append(function)
+ f.close()
+
+ return retval
+
+ def _symbol_list(self, symbol_file):
+ retval = []
+ f = open(symbol_file, 'r')
+ for line in f.readlines():
+ match = self._constant_re.match(line)
+ if match:
+ retval.append(line.strip('\n'))
+ continue
+
+ match = self._function_re.match(line)
+ if not match or match.group(1).endswith('get_type'):
+ continue
+ retval.append(match.group(1))
+
+ return retval
+
+ def write_section(self, symbol_file):
+ class_name = os.path.basename(symbol_file).replace(".symbols", "")
+ is_custom = class_name == 'WebKitDOMCustom'
+ is_interface = class_name in ['WebKitDOMEventTarget', 'WebKitDOMNodeFilter', 'WebKitDOMXPathNSResolver']
+ is_object = class_name == 'WebKitDOMObject'
+ self.write('<SECTION>\n')
+ self.write('<FILE>%s</FILE>\n<TITLE>%s</TITLE>\n' % (class_name, class_name))
+ if not is_custom:
+ self.write('%s\n' % class_name)
+ self.write('\n')
+ self.write('\n'.join(self._symbol_list(symbol_file)) + '\n')
+ try:
+ deprecated_functions = self._deprecated_symbols[class_name]
+ self.write('\n'.join(deprecated_functions) + '\n')
+ except KeyError:
+ pass
+ if not is_custom:
+ self.write('\n<SUBSECTION Standard>\n')
+ if is_interface:
+ self.write('%sIface\n' % class_name)
+ else:
+ self.write('%sClass\n' % class_name)
+ dom_class = self._dom_class_decamelize(class_name).upper()
+ self.write('WEBKIT_DOM_TYPE_%s\n' % dom_class)
+ self.write('WEBKIT_DOM_%s\n' % dom_class)
+ self.write('WEBKIT_DOM_IS_%s\n' % dom_class)
+ self.write('WEBKIT_DOM_%s_CLASS\n' % dom_class)
+ if is_interface:
+ self.write('WEBKIT_DOM_%s_GET_IFACE\n' % dom_class)
+ else:
+ self.write('WEBKIT_DOM_IS_%s_CLASS\n' % dom_class)
+ self.write('WEBKIT_DOM_%s_GET_CLASS\n' % dom_class)
+ self.write('\n<SUBSECTION Private>\n')
+ if is_object:
+ self.write('%sPrivate\n' % class_name)
+ self.write('webkit_dom_%s_get_type\n' % dom_class.lower())
+ self.write('</SECTION>\n\n')
+
+ def write_footer(self):
+ self.write('<SECTION>\n')
+ self.write('<FILE>webkitdomdefines</FILE>\n<TITLE>WebKitDOMDefines</TITLE>\n')
+ self.write('<SUBSECTION Private>\n')
+ self.write('WEBKIT_API\nWEBKIT_DEPRECATED\nWEBKIT_DEPRECATED_FOR\n')
+ self.write('</SECTION>\n\n')
+
+
+def write_doc_files(module_name):
+ doc_dir = common.build_path('DerivedSources', 'webkitdom', 'docs')
+
+ try:
+ os.mkdir(doc_dir)
+ except OSError, e:
+ if e.errno != errno.EEXIST or not os.path.isdir(doc_dir):
+ sys.stderr.write("Could not create doc dir at %s: %s\n" % (doc_dir, str(e)))
+ sys.exit(1)
+
+ with open(os.path.join(doc_dir, '%s-sections.txt' % module_name), 'w') as sections_file:
+ generator = WebKitDOMDocGeneratorSections(get_all_webkitdom_symbol_files(), sections_file)
+ generator.generate()
+ with open(os.path.join(doc_dir, 'webkitdomgtk-docs.sgml'), 'w') as sgml_file:
+ generator = WebKitDOMDocGeneratorSGML(get_all_webkitdom_symbol_files(), sgml_file)
+ generator.generate()
+
+
+def header_name_list_from_gtkdoc_config_file():
+ config_file = common.build_path('gtkdoc-webkitdom.cfg')
+ if not os.path.isfile(config_file):
+ sys.stderr.write("Could not find config file at %s\n" % config_file)
+ return sys.exit(1)
+
+ config = SafeConfigParser()
+ config.read(config_file)
+ module_name = config.sections()[0]
+ return [os.path.basename(f) for f in str(config.get(module_name, 'headers')).replace(';', ' ').split()]
+
+
+def get_all_webkitdom_symbol_files():
+ static_symbol_files_path = common.top_level_path('Source', 'WebCore', 'bindings', 'gobject')
+ generated_symbol_files_path = common.build_path('DerivedSources', 'webkitdom')
+
+ symbol_files = []
+ for header_name in header_name_list_from_gtkdoc_config_file():
+ # webkitdomdefines.h doesn't have a corresponding symbols file and webkitdom.symbols is a
+ # file containing the expected symbols results.
+ if header_name in ("webkitdom.h", "webkitdomdefines.h"):
+ continue
+
+ symbol_file = header_name.replace(".h", ".symbols")
+ path = os.path.join(static_symbol_files_path, symbol_file)
+ if os.path.exists(path):
+ symbol_files.append(path)
+ continue
+ path = os.path.join(generated_symbol_files_path, symbol_file)
+ if os.path.exists(path):
+ symbol_files.append(path)
+ continue
+
+ return symbol_files
diff --git a/Tools/gtk/ycm_extra_conf.py b/Tools/gtk/ycm_extra_conf.py
new file mode 100644
index 000000000..eb35c945d
--- /dev/null
+++ b/Tools/gtk/ycm_extra_conf.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+# Copyright (C) 2013 Danilo Cesar Lemes de Paula <danilo.eu@gmail.com>
+# Copyright (C) 2014 ChangSeok Oh <shivamidow@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import os
+import sys
+import ycm_core
+
+# It's very likely that this script is a symlink somewhere in the WebKit directory,
+# so we try to find the actual script location so that we can locate the tools
+# directory.
+original_file = __file__[:-1] if __file__.endswith(".pyc") else __file__
+if os.path.islink(original_file):
+ parent_folder = os.path.abspath(os.path.dirname(original_file))
+ link_file = os.path.join(parent_folder, os.readlink(original_file))
+ __tools_directory = os.path.dirname(link_file)
+else:
+ __tools_directory = os.path.dirname(original_file)
+
+sys.path.insert(0, os.path.abspath(__tools_directory))
+import common
+
+
+FLAGS_PRECEDING_PATHS = ['-isystem', '-I', '-iquote', '--sysroot=']
+def transform_relative_paths_to_absolute_paths(arguments, build_path):
+ result = []
+ make_next_absolute = False
+ for argument in arguments:
+ if make_next_absolute:
+ make_next_absolute = False
+ if not argument.startswith('/'):
+ argument = os.path.join(build_path, argument)
+ elif argument in FLAGS_PRECEDING_PATHS:
+ # Some flags precede the path in the list. For those we make the
+ # next argument absolute.
+ if argument == flag:
+ make_next_absolute = True
+ else:
+ # Some argument contain the flag and the path together. For these
+ # we parse the argument out of the path.
+ for flag in FLAGS_PRECEDING_PATHS:
+ if argument.startswith(flag):
+ argument = flag + os.path.join(build_path, argument[len(flag):])
+ break
+
+ result.append(argument)
+ return result
+
+
+def get_build_path():
+ webkitbuild_path = os.path.join(common.get_build_path(fatal=False), '..')
+ if not os.path.exists(webkitbuild_path):
+ return None
+
+ release_build_path = os.path.join(webkitbuild_path, 'Release')
+ debug_build_path = os.path.join(webkitbuild_path, 'Debug')
+
+ try:
+ release_mtime = os.path.getmtime(os.path.join(release_build_path, 'compile_commands.json'))
+ except os.error:
+ release_mtime = 0
+ try:
+ debug_mtime = os.path.getmtime(os.path.join(debug_build_path, 'compile_commands.json'))
+ except os.error:
+ debug_mtime = 0
+
+ return release_build_path if release_mtime >= debug_mtime else debug_build_path
+
+
+def FlagsForFile(filename, **kwargs):
+ """This is the main entry point for YCM. Its interface is fixed.
+
+ Args:
+ filename: (String) Path to source file being edited.
+
+ Returns:
+ (Dictionary)
+ 'flags': (List of Strings) Command line flags.
+ 'do_cache': (Boolean) True if the result should be cached.
+ """
+
+ result = {'flags': ['-std=c++11', '-x', 'c++'], 'do_cache': True}
+
+ # Headers can't be built, so we get the source file flags instead.
+ if filename.endswith('.h'):
+ alternative_extensions = ['.cpp', '.c']
+ for alternative_extension in alternative_extensions:
+ alternative_filename = filename[:-2] + alternative_extension
+ if os.path.exists(alternative_filename):
+ filename = alternative_filename
+ break
+ else:
+ return result
+ # Force config.h file inclusion, for GLib macros.
+ result['flags'].append("-includeconfig.h")
+
+ build_path = os.path.normpath(get_build_path())
+ if not build_path:
+ print "Could not find WebKit build path."
+ return result
+
+ database = ycm_core.CompilationDatabase(build_path)
+ if not database:
+ print "Could not find compile_commands.json in %s, You might forget to add CMAKE_EXPORT_COMPILE_COMMANDS=ON to cmakeargs" % build_path
+ return result
+
+ compilation_info = database.GetCompilationInfoForFile(filename)
+ if not compilation_info:
+ print "No compilation info."
+ return result
+
+ result['flags'] = transform_relative_paths_to_absolute_paths(list(compilation_info.compiler_flags_), compilation_info.compiler_working_dir_)
+ return result
+
+
+if __name__ == "__main__":
+ import sys
+ if len(sys.argv) >= 2:
+ print FlagsForFile(sys.argv[1])
diff --git a/Tools/jhbuild/jhbuildutils.py b/Tools/jhbuild/jhbuildutils.py
new file mode 100644
index 000000000..ed0022ea3
--- /dev/null
+++ b/Tools/jhbuild/jhbuildutils.py
@@ -0,0 +1,54 @@
+import glob
+import os.path
+import sys
+import __builtin__
+
+top_level_dir = None
+
+
+def top_level_path(*args):
+ global top_level_dir
+ if not top_level_dir:
+ top_level_dir = os.path.join(os.path.dirname(__file__), '..', '..')
+ return os.path.join(*(top_level_dir,) + args)
+
+
+def get_dependencies_path(platform):
+ dependencies_dir = "%s%s" % ('Dependencies', platform.upper())
+ if 'WEBKIT_OUTPUTDIR' in os.environ:
+ return os.path.abspath(os.path.join(os.environ['WEBKIT_OUTPUTDIR'], dependencies_dir))
+ else:
+ return os.path.abspath(top_level_path('WebKitBuild', dependencies_dir))
+
+
+def get_config_file_for_platform(platform):
+ return top_level_path('Tools', platform, 'jhbuildrc')
+
+
+def enter_jhbuild_environment_if_available(platform):
+ if not os.path.exists(get_dependencies_path(platform)):
+ return False
+
+ # Sometimes jhbuild chooses to install in a way that reads the library from the source directory, so fall
+ # back to that method.
+ source_path = os.path.join(get_dependencies_path(platform), "Source", "jhbuild")
+ sys.path.insert(0, source_path)
+
+ # When loading jhbuild from the source checkout it fails if the SRCDIR variable is not set.
+ __builtin__.__dict__['SRCDIR'] = source_path
+
+ # We don't know the Python version, so we just assume that we can safely take the first one in the list.
+ site_packages_path = glob.glob(os.path.join(get_dependencies_path(platform), "Root", "lib", "*", "site-packages"))
+ if len(site_packages_path):
+ site_packages_path = site_packages_path[0]
+ sys.path.insert(0, site_packages_path)
+
+ try:
+ import jhbuild.config
+ from jhbuild.errors import FatalError
+ config = jhbuild.config.Config(get_config_file_for_platform(platform))
+ except FatalError, exception:
+ sys.stderr.write('Could not load jhbuild config file: %s\n' % exception.args[0])
+ return False
+
+ return True