summaryrefslogtreecommitdiff
path: root/Tools/TestWebKitAPI
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/TestWebKitAPI')
-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
292 files changed, 42790 insertions, 0 deletions
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;
+}