| /* |
| * Copyright (C) 2014-2020 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "config.h" |
| #import "WKWebViewPrivateForTesting.h" |
| |
| #import "AudioSessionRoutingArbitratorProxy.h" |
| #import "GPUProcessProxy.h" |
| #import "MediaSessionCoordinatorProxyPrivate.h" |
| #import "PlaybackSessionManagerProxy.h" |
| #import "UserMediaProcessManager.h" |
| #import "ViewGestureController.h" |
| #import "WebPageProxy.h" |
| #import "WebProcessPool.h" |
| #import "WebProcessProxy.h" |
| #import "WebViewImpl.h" |
| #import "_WKFrameHandleInternal.h" |
| #import "_WKInspectorInternal.h" |
| #import <WebCore/RuntimeApplicationChecks.h> |
| #import <WebCore/ValidationBubble.h> |
| #import <wtf/RetainPtr.h> |
| |
| #if PLATFORM(MAC) |
| #import "WKWebViewMac.h" |
| #endif |
| |
| #if PLATFORM(IOS_FAMILY) |
| #import "WKWebViewIOS.h" |
| #endif |
| |
| #if ENABLE(MEDIA_SESSION_COORDINATOR) |
| @interface WKMediaSessionCoordinatorHelper : NSObject <_WKMediaSessionCoordinatorDelegate> |
| - (id)initWithCoordinator:(WebCore::MediaSessionCoordinatorClient*)coordinator; |
| - (void)seekSessionToTime:(double)time withCompletion:(void(^)(BOOL))completionHandler; |
| - (void)playSessionWithCompletion:(void(^)(BOOL))completionHandler; |
| - (void)pauseSessionWithCompletion:(void(^)(BOOL))completionHandler; |
| - (void)setSessionTrack:(NSString*)trackIdentifier withCompletion:(void(^)(BOOL))completionHandler; |
| - (void)coordinatorStateChanged:(_WKMediaSessionCoordinatorState)state; |
| @end |
| #endif |
| |
| @implementation WKWebView (WKTesting) |
| |
| - (void)_addEventAttributionWithSourceID:(uint8_t)sourceID destinationURL:(NSURL *)destination sourceDescription:(NSString *)sourceDescription purchaser:(NSString *)purchaser reportEndpoint:(NSURL *)reportEndpoint optionalNonce:(NSString *)nonce applicationBundleID:(NSString *)bundleID ephemeral:(BOOL)ephemeral |
| { |
| WebCore::PrivateClickMeasurement measurement( |
| WebCore::PrivateClickMeasurement::SourceID(sourceID), |
| WebCore::PrivateClickMeasurement::SourceSite(reportEndpoint), |
| WebCore::PrivateClickMeasurement::AttributionDestinationSite(destination), |
| bundleID, |
| WallTime::now(), |
| ephemeral ? WebCore::PrivateClickMeasurement::AttributionEphemeral::Yes : WebCore::PrivateClickMeasurement::AttributionEphemeral::No |
| ); |
| if (nonce) |
| measurement.setEphemeralSourceNonce({ nonce }); |
| |
| _page->setPrivateClickMeasurement({{ WTFMove(measurement), { }, { }}}); |
| } |
| |
| - (void)_setPageScale:(CGFloat)scale withOrigin:(CGPoint)origin |
| { |
| _page->scalePage(scale, WebCore::roundedIntPoint(origin)); |
| } |
| |
| - (CGFloat)_pageScale |
| { |
| return _page->pageScaleFactor(); |
| } |
| |
| - (void)_setContinuousSpellCheckingEnabledForTesting:(BOOL)enabled |
| { |
| #if PLATFORM(IOS_FAMILY) |
| [_contentView setContinuousSpellCheckingEnabled:enabled]; |
| #else |
| _impl->setContinuousSpellCheckingEnabled(enabled); |
| #endif |
| } |
| |
| - (NSDictionary *)_contentsOfUserInterfaceItem:(NSString *)userInterfaceItem |
| { |
| if ([userInterfaceItem isEqualToString:@"validationBubble"]) { |
| auto* validationBubble = _page->validationBubble(); |
| String message = validationBubble ? validationBubble->message() : emptyString(); |
| double fontSize = validationBubble ? validationBubble->fontSize() : 0; |
| return @{ userInterfaceItem: @{ @"message": (NSString *)message, @"fontSize": @(fontSize) } }; |
| } |
| |
| if (NSDictionary *contents = _page->contentsOfUserInterfaceItem(userInterfaceItem)) |
| return contents; |
| |
| #if PLATFORM(IOS_FAMILY) |
| return [_contentView _contentsOfUserInterfaceItem:(NSString *)userInterfaceItem]; |
| #else |
| return nil; |
| #endif |
| } |
| |
| - (void)_requestActiveNowPlayingSessionInfo:(void(^)(BOOL, BOOL, NSString*, double, double, NSInteger))callback |
| { |
| if (!_page) { |
| callback(NO, NO, @"", std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), 0); |
| return; |
| } |
| |
| _page->requestActiveNowPlayingSessionInfo([handler = makeBlockPtr(callback)] (bool active, bool registeredAsNowPlayingApplication, String title, double duration, double elapsedTime, uint64_t uniqueIdentifier) { |
| handler(active, registeredAsNowPlayingApplication, title, duration, elapsedTime, uniqueIdentifier); |
| }); |
| } |
| |
| - (BOOL)_scrollingUpdatesDisabledForTesting |
| { |
| // For subclasses to override; |
| return NO; |
| } |
| |
| - (void)_setScrollingUpdatesDisabledForTesting:(BOOL)disabled |
| { |
| } |
| |
| - (void)_doAfterNextPresentationUpdateWithoutWaitingForAnimatedResizeForTesting:(void (^)(void))updateBlock |
| { |
| [self _internalDoAfterNextPresentationUpdate:updateBlock withoutWaitingForPainting:NO withoutWaitingForAnimatedResize:YES]; |
| } |
| |
| - (void)_doAfterNextVisibleContentRectUpdate:(void (^)(void))updateBlock |
| { |
| #if PLATFORM(IOS_FAMILY) |
| _visibleContentRectUpdateCallbacks.append(makeBlockPtr(updateBlock)); |
| [self _scheduleVisibleContentRectUpdate]; |
| #else |
| RunLoop::main().dispatch([updateBlock = makeBlockPtr(updateBlock)] { |
| updateBlock(); |
| }); |
| #endif |
| } |
| |
| - (void)_disableBackForwardSnapshotVolatilityForTesting |
| { |
| WebKit::ViewSnapshotStore::singleton().setDisableSnapshotVolatilityForTesting(true); |
| } |
| |
| - (BOOL)_beginBackSwipeForTesting |
| { |
| #if PLATFORM(MAC) |
| return _impl->beginBackSwipeForTesting(); |
| #else |
| if (!_gestureController) |
| return NO; |
| return _gestureController->beginSimulatedSwipeInDirectionForTesting(WebKit::ViewGestureController::SwipeDirection::Back); |
| #endif |
| } |
| |
| - (BOOL)_completeBackSwipeForTesting |
| { |
| #if PLATFORM(MAC) |
| return _impl->completeBackSwipeForTesting(); |
| #else |
| if (!_gestureController) |
| return NO; |
| return _gestureController->completeSimulatedSwipeInDirectionForTesting(WebKit::ViewGestureController::SwipeDirection::Back); |
| #endif |
| } |
| |
| - (void)_resetNavigationGestureStateForTesting |
| { |
| #if PLATFORM(MAC) |
| if (auto gestureController = _impl->gestureController()) |
| gestureController->reset(); |
| #else |
| if (_gestureController) |
| _gestureController->reset(); |
| #endif |
| } |
| |
| - (void)_setDefersLoadingForTesting:(BOOL)defersLoading |
| { |
| _page->setDefersLoadingForTesting(defersLoading); |
| } |
| |
| - (void)_setShareSheetCompletesImmediatelyWithResolutionForTesting:(BOOL)resolved |
| { |
| _resolutionForShareSheetImmediateCompletionForTesting = resolved; |
| } |
| |
| - (void)_processWillSuspendForTesting:(void (^)(void))completionHandler |
| { |
| if (!_page) { |
| completionHandler(); |
| return; |
| } |
| _page->process().sendPrepareToSuspend(WebKit::IsSuspensionImminent::No, [completionHandler = makeBlockPtr(completionHandler)] { |
| completionHandler(); |
| }); |
| } |
| |
| - (void)_processWillSuspendImminentlyForTesting |
| { |
| if (_page) |
| _page->process().sendPrepareToSuspend(WebKit::IsSuspensionImminent::Yes, [] { }); |
| } |
| |
| - (void)_processDidResumeForTesting |
| { |
| if (_page) |
| _page->process().sendProcessDidResume(); |
| } |
| |
| - (void)_setAssertionTypeForTesting:(int)value |
| { |
| if (!_page) |
| return; |
| |
| _page->process().setAssertionTypeForTesting(static_cast<WebKit::ProcessAssertionType>(value)); |
| } |
| |
| - (BOOL)_hasServiceWorkerBackgroundActivityForTesting |
| { |
| #if ENABLE(SERVICE_WORKER) |
| return _page ? _page->process().processPool().hasServiceWorkerBackgroundActivityForTesting() : false; |
| #else |
| return false; |
| #endif |
| } |
| |
| - (BOOL)_hasServiceWorkerForegroundActivityForTesting |
| { |
| #if ENABLE(SERVICE_WORKER) |
| return _page ? _page->process().processPool().hasServiceWorkerForegroundActivityForTesting() : false; |
| #else |
| return false; |
| #endif |
| } |
| |
| - (void)_denyNextUserMediaRequest |
| { |
| #if ENABLE(MEDIA_STREAM) |
| WebKit::UserMediaProcessManager::singleton().denyNextUserMediaRequest(); |
| #endif |
| } |
| |
| - (void)_setIndexOfGetDisplayMediaDeviceSelectedForTesting:(NSNumber *)nsIndex |
| { |
| #if HAVE(SCREEN_CAPTURE_KIT) |
| if (!_page) |
| return; |
| |
| std::optional<unsigned> index; |
| if (nsIndex) |
| index = nsIndex.unsignedIntValue; |
| |
| _page->setIndexOfGetDisplayMediaDeviceSelectedForTesting(index); |
| #endif |
| } |
| |
| - (double)_mediaCaptureReportingDelayForTesting |
| { |
| return _page->mediaCaptureReportingDelay().value(); |
| } |
| |
| - (void)_setMediaCaptureReportingDelayForTesting:(double)captureReportingDelay |
| { |
| _page->setMediaCaptureReportingDelay(Seconds(captureReportingDelay)); |
| } |
| |
| - (BOOL)_wirelessVideoPlaybackDisabled |
| { |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| if (auto* playbackSessionManager = _page->playbackSessionManager()) |
| return playbackSessionManager->wirelessVideoPlaybackDisabled(); |
| #endif |
| return false; |
| } |
| |
| - (void)_doAfterProcessingAllPendingMouseEvents:(dispatch_block_t)action |
| { |
| _page->doAfterProcessingAllPendingMouseEvents([action = makeBlockPtr(action)] { |
| action(); |
| }); |
| } |
| |
| + (void)_setApplicationBundleIdentifier:(NSString *)bundleIdentifier |
| { |
| WebCore::setApplicationBundleIdentifierOverride(String(bundleIdentifier)); |
| } |
| |
| + (void)_clearApplicationBundleIdentifierTestingOverride |
| { |
| WebCore::clearApplicationBundleIdentifierTestingOverride(); |
| } |
| |
| - (BOOL)_hasSleepDisabler |
| { |
| return _page && _page->process().hasSleepDisabler(); |
| } |
| |
| - (WKWebViewAudioRoutingArbitrationStatus)_audioRoutingArbitrationStatus |
| { |
| #if ENABLE(ROUTING_ARBITRATION) |
| switch (_page->process().audioSessionRoutingArbitrator().arbitrationStatus()) { |
| case WebKit::AudioSessionRoutingArbitratorProxy::ArbitrationStatus::None: return WKWebViewAudioRoutingArbitrationStatusNone; |
| case WebKit::AudioSessionRoutingArbitratorProxy::ArbitrationStatus::Pending: return WKWebViewAudioRoutingArbitrationStatusPending; |
| case WebKit::AudioSessionRoutingArbitratorProxy::ArbitrationStatus::Active: return WKWebViewAudioRoutingArbitrationStatusActive; |
| default: ASSERT_NOT_REACHED(); |
| } |
| #else |
| return WKWebViewAudioRoutingArbitrationStatusNone; |
| #endif |
| } |
| |
| - (double)_audioRoutingArbitrationUpdateTime |
| { |
| #if ENABLE(ROUTING_ARBITRATION) |
| return _page->process().audioSessionRoutingArbitrator().arbitrationUpdateTime().secondsSinceEpoch().seconds(); |
| #else |
| return 0; |
| #endif |
| } |
| |
| - (void)_doAfterActivityStateUpdate:(void (^)(void))completionHandler |
| { |
| _page->addActivityStateUpdateCompletionHandler(makeBlockPtr(completionHandler)); |
| } |
| |
| - (NSNumber *)_suspendMediaPlaybackCounter |
| { |
| return @(_page->suspendMediaPlaybackCounter()); |
| } |
| |
| - (void)_setPrivateClickMeasurementOverrideTimerForTesting:(BOOL)overrideTimer completionHandler:(void(^)(void))completionHandler |
| { |
| _page->setPrivateClickMeasurementOverrideTimerForTesting(overrideTimer, [completionHandler = makeBlockPtr(completionHandler)] { |
| completionHandler(); |
| }); |
| } |
| |
| - (void)_setPrivateClickMeasurementAttributionReportURLsForTesting:(NSURL *)sourceURL destinationURL:(NSURL *)destinationURL completionHandler:(void(^)(void))completionHandler |
| { |
| _page->setPrivateClickMeasurementAttributionReportURLsForTesting(sourceURL, destinationURL, [completionHandler = makeBlockPtr(completionHandler)] { |
| completionHandler(); |
| }); |
| } |
| |
| - (void)_setPrivateClickMeasurementAttributionTokenPublicKeyURLForTesting:(NSURL *)url completionHandler:(void(^)(void))completionHandler |
| { |
| _page->setPrivateClickMeasurementTokenPublicKeyURLForTesting(url, [completionHandler = makeBlockPtr(completionHandler)] { |
| completionHandler(); |
| }); |
| } |
| |
| - (void)_setPrivateClickMeasurementAttributionTokenSignatureURLForTesting:(NSURL *)url completionHandler:(void(^)(void))completionHandler |
| { |
| _page->setPrivateClickMeasurementTokenSignatureURLForTesting(url, [completionHandler = makeBlockPtr(completionHandler)] { |
| completionHandler(); |
| }); |
| } |
| |
| - (void)_setPrivateClickMeasurementAppBundleIDForTesting:(NSString *)appBundleID completionHandler:(void(^)(void))completionHandler |
| { |
| _page->setPrivateClickMeasurementAppBundleIDForTesting(appBundleID, [completionHandler = makeBlockPtr(completionHandler)] { |
| completionHandler(); |
| }); |
| } |
| |
| - (void)_dumpPrivateClickMeasurement:(void(^)(NSString *))completionHandler |
| { |
| _page->dumpPrivateClickMeasurement([completionHandler = makeBlockPtr(completionHandler)](const String& privateClickMeasurement) { |
| completionHandler(privateClickMeasurement); |
| }); |
| } |
| |
| - (void)_didShowContextMenu |
| { |
| // For subclasses to override. |
| } |
| |
| - (void)_didDismissContextMenu |
| { |
| // For subclasses to override. |
| } |
| |
| - (void)_didPresentContactPicker |
| { |
| // For subclasses to override. |
| } |
| |
| - (void)_didDismissContactPicker |
| { |
| // For subclasses to override. |
| } |
| |
| - (void)_dismissContactPickerWithContacts:(NSArray *)contacts |
| { |
| #if PLATFORM(IOS_FAMILY) |
| [_contentView _dismissContactPickerWithContacts:contacts]; |
| #endif |
| } |
| |
| - (void)_lastNavigationWasAppInitiated:(void(^)(BOOL))completionHandler |
| { |
| _page->lastNavigationWasAppInitiated([completionHandler = makeBlockPtr(completionHandler)] (bool isAppInitiated) { |
| completionHandler(isAppInitiated); |
| }); |
| } |
| |
| - (void)_appPrivacyReportTestingData:(void(^)(struct WKAppPrivacyReportTestingData data))completionHandler |
| { |
| _page->appPrivacyReportTestingData([completionHandler = makeBlockPtr(completionHandler)] (auto&& appPrivacyReportTestingData) { |
| completionHandler({ appPrivacyReportTestingData.hasLoadedAppInitiatedRequestTesting, appPrivacyReportTestingData.hasLoadedNonAppInitiatedRequestTesting, appPrivacyReportTestingData.didPerformSoftUpdate }); |
| }); |
| } |
| |
| - (void)_clearAppPrivacyReportTestingData:(void(^)(void))completionHandler |
| { |
| _page->clearAppPrivacyReportTestingData([completionHandler = makeBlockPtr(completionHandler)] { |
| completionHandler(); |
| }); |
| } |
| |
| - (void)_isLayerTreeFrozenForTesting:(void (^)(BOOL frozen))completionHandler |
| { |
| _page->isLayerTreeFrozen([completionHandler = makeBlockPtr(completionHandler)](bool isFrozen) { |
| completionHandler(isFrozen); |
| }); |
| } |
| |
| - (void)_gpuToWebProcessConnectionCountForTesting:(void(^)(NSUInteger))completionHandler |
| { |
| RefPtr gpuProcess = _page->process().processPool().gpuProcess(); |
| if (!gpuProcess) { |
| completionHandler(0); |
| return; |
| } |
| |
| gpuProcess->webProcessConnectionCountForTesting([completionHandler = makeBlockPtr(completionHandler)](uint64_t count) { |
| completionHandler(count); |
| }); |
| } |
| |
| - (void)_createMediaSessionCoordinatorForTesting:(id <_WKMediaSessionCoordinator>)privateCoordinator completionHandler:(void(^)(BOOL))completionHandler |
| { |
| #if ENABLE(MEDIA_SESSION_COORDINATOR) |
| class WKMediaSessionCoordinatorForTesting |
| : public WebKit::MediaSessionCoordinatorProxyPrivate |
| , public WebCore::MediaSessionCoordinatorClient { |
| WTF_MAKE_FAST_ALLOCATED; |
| public: |
| |
| static Ref<WKMediaSessionCoordinatorForTesting> create(id <_WKMediaSessionCoordinator> privateCoordinator) |
| { |
| return adoptRef(*new WKMediaSessionCoordinatorForTesting(privateCoordinator)); |
| } |
| |
| using WebCore::MediaSessionCoordinatorClient::weakPtrFactory; |
| using WeakValueType = WebCore::MediaSessionCoordinatorClient::WeakValueType; |
| |
| private: |
| explicit WKMediaSessionCoordinatorForTesting(id <_WKMediaSessionCoordinator> clientCoordinator) |
| : WebKit::MediaSessionCoordinatorProxyPrivate() |
| , m_clientCoordinator(clientCoordinator) |
| { |
| auto* delegateHelper = [[WKMediaSessionCoordinatorHelper alloc] initWithCoordinator:this]; |
| [m_clientCoordinator setDelegate:delegateHelper]; |
| m_coordinatorDelegate = adoptNS(delegateHelper); |
| } |
| |
| void seekSessionToTime(double time, CompletionHandler<void(bool)>&& callback) final |
| { |
| if (auto coordinatorClient = client()) |
| coordinatorClient->seekSessionToTime(time, WTFMove(callback)); |
| else |
| callback(false); |
| } |
| |
| void playSession(std::optional<double> atTime, std::optional<MonotonicTime> hostTime, CompletionHandler<void(bool)>&& callback) final |
| { |
| if (auto coordinatorClient = client()) |
| coordinatorClient->playSession(WTFMove(atTime), WTFMove(hostTime), WTFMove(callback)); |
| else |
| callback(false); |
| } |
| |
| void pauseSession(CompletionHandler<void(bool)>&& callback) final |
| { |
| if (auto coordinatorClient = client()) |
| coordinatorClient->pauseSession(WTFMove(callback)); |
| else |
| callback(false); |
| } |
| |
| void setSessionTrack(const String& trackIdentifier, CompletionHandler<void(bool)>&& callback) final |
| { |
| if (auto coordinatorClient = client()) |
| coordinatorClient->setSessionTrack(trackIdentifier, WTFMove(callback)); |
| else |
| callback(false); |
| } |
| |
| void coordinatorStateChanged(WebCore::MediaSessionCoordinatorState state) final |
| { |
| if (auto coordinatorClient = client()) |
| coordinatorClient->coordinatorStateChanged(state); |
| } |
| |
| std::optional<WebCore::ExceptionData> result(bool success) const |
| { |
| if (!success) |
| return { WebCore::ExceptionData { WebCore::InvalidStateError, String() } }; |
| |
| return { }; |
| } |
| |
| String identifier() const |
| { |
| return [m_clientCoordinator identifier]; |
| } |
| |
| void join(WebKit::MediaSessionCommandCompletionHandler&& callback) final |
| { |
| [m_clientCoordinator joinWithCompletion:makeBlockPtr([weakThis = WeakPtr { *this }, callback = WTFMove(callback)] (BOOL success) mutable { |
| if (!weakThis) { |
| callback(WebCore::ExceptionData { WebCore::InvalidStateError, String() }); |
| return; |
| } |
| |
| callback(weakThis->result(success)); |
| }).get()]; |
| } |
| |
| void leave() final |
| { |
| [m_clientCoordinator leave]; |
| } |
| |
| void seekTo(double time, WebKit::MediaSessionCommandCompletionHandler&& callback) final |
| { |
| [m_clientCoordinator seekTo:time withCompletion:makeBlockPtr([weakThis = WeakPtr { *this }, callback = WTFMove(callback)] (BOOL success) mutable { |
| if (!weakThis) { |
| callback(WebCore::ExceptionData { WebCore::InvalidStateError, String() }); |
| return; |
| } |
| |
| callback(weakThis->result(success)); |
| }).get()]; |
| } |
| |
| void play(WebKit::MediaSessionCommandCompletionHandler&& callback) final |
| { |
| [m_clientCoordinator playWithCompletion:makeBlockPtr([weakThis = WeakPtr { *this }, callback = WTFMove(callback)] (BOOL success) mutable { |
| if (!weakThis) { |
| callback(WebCore::ExceptionData { WebCore::InvalidStateError, String() }); |
| return; |
| } |
| |
| callback(weakThis->result(success)); |
| }).get()]; |
| } |
| |
| void pause(WebKit::MediaSessionCommandCompletionHandler&& callback) final |
| { |
| [m_clientCoordinator pauseWithCompletion:makeBlockPtr([weakThis = WeakPtr { *this }, callback = WTFMove(callback)] (BOOL success) mutable { |
| if (!weakThis) { |
| callback(WebCore::ExceptionData { WebCore::InvalidStateError, String() }); |
| return; |
| } |
| |
| callback(weakThis->result(success)); |
| }).get()]; |
| } |
| |
| void setTrack(const String& track, WebKit::MediaSessionCommandCompletionHandler&& callback) final |
| { |
| [m_clientCoordinator setTrack:track withCompletion:makeBlockPtr([weakThis = WeakPtr { *this }, callback = WTFMove(callback)] (BOOL success) mutable { |
| if (!weakThis) { |
| callback(WebCore::ExceptionData { WebCore::InvalidStateError, String() }); |
| return; |
| } |
| |
| callback(weakThis->result(success)); |
| }).get()]; |
| } |
| |
| void positionStateChanged(const std::optional<WebCore::MediaPositionState>& state) final |
| { |
| if (!state) { |
| [m_clientCoordinator positionStateChanged:nil]; |
| return; |
| } |
| |
| _WKMediaPositionState position = { |
| .duration = state->duration, |
| .playbackRate = state->playbackRate, |
| .position = state->position |
| }; |
| [m_clientCoordinator positionStateChanged:&position]; |
| } |
| |
| void readyStateChanged(WebCore::MediaSessionReadyState state) final |
| { |
| static_assert(static_cast<size_t>(WebCore::MediaSessionReadyState::Havenothing) == static_cast<size_t>(WKMediaSessionReadyStateHaveNothing), "WKMediaSessionReadyStateHaveNothing does not match WebKit value"); |
| static_assert(static_cast<size_t>(WebCore::MediaSessionReadyState::Havemetadata) == static_cast<size_t>(WKMediaSessionReadyStateHaveMetadata), "WKMediaSessionReadyStateHaveMetadata does not match WebKit value"); |
| static_assert(static_cast<size_t>(WebCore::MediaSessionReadyState::Havecurrentdata) == static_cast<size_t>(WKMediaSessionReadyStateHaveCurrentData), "WKMediaSessionReadyStateHaveCurrentData does not match WebKit value"); |
| static_assert(static_cast<size_t>(WebCore::MediaSessionReadyState::Havefuturedata) == static_cast<size_t>(WKMediaSessionReadyStateHaveFutureData), "WKMediaSessionReadyStateHaveFutureData does not match WebKit value"); |
| static_assert(static_cast<size_t>(WebCore::MediaSessionReadyState::Haveenoughdata) == static_cast<size_t>(WKMediaSessionReadyStateHaveEnoughData), "WKMediaSessionReadyStateHaveEnoughData does not match WebKit value"); |
| |
| [m_clientCoordinator readyStateChanged:static_cast<_WKMediaSessionReadyState>(state)]; |
| } |
| |
| void playbackStateChanged(WebCore::MediaSessionPlaybackState state) final |
| { |
| static_assert(static_cast<size_t>(WebCore::MediaSessionPlaybackState::None) == static_cast<size_t>(WKMediaSessionPlaybackStateNone), "WKMediaSessionPlaybackStateNone does not match WebKit value"); |
| static_assert(static_cast<size_t>(WebCore::MediaSessionPlaybackState::Paused) == static_cast<size_t>(WKMediaSessionPlaybackStatePaused), "WKMediaSessionPlaybackStatePaused does not match WebKit value"); |
| static_assert(static_cast<size_t>(WebCore::MediaSessionPlaybackState::Playing) == static_cast<size_t>(WKMediaSessionPlaybackStatePlaying), "WKMediaSessionPlaybackStatePlaying does not match WebKit value"); |
| |
| [m_clientCoordinator playbackStateChanged:static_cast<_WKMediaSessionPlaybackState>(state)]; |
| } |
| |
| void trackIdentifierChanged(const String& identifier) final |
| { |
| [m_clientCoordinator trackIdentifierChanged:identifier]; |
| } |
| |
| private: |
| RetainPtr<id <_WKMediaSessionCoordinator>> m_clientCoordinator; |
| RetainPtr<WKMediaSessionCoordinatorHelper> m_coordinatorDelegate; |
| }; |
| |
| ASSERT(!_impl->mediaSessionCoordinatorForTesting()); |
| |
| _impl->setMediaSessionCoordinatorForTesting(WKMediaSessionCoordinatorForTesting::create(privateCoordinator).ptr()); |
| _impl->page().createMediaSessionCoordinator(*_impl->mediaSessionCoordinatorForTesting(), [completionHandler = makeBlockPtr(completionHandler)] (bool success) { |
| completionHandler(success); |
| }); |
| #else |
| |
| completionHandler(NO); |
| |
| #endif |
| } |
| |
| @end |
| |
| #if ENABLE(MEDIA_SESSION_COORDINATOR) |
| @implementation WKMediaSessionCoordinatorHelper { |
| WeakPtr<WebCore::MediaSessionCoordinatorClient> m_coordinatorClient; |
| } |
| |
| - (id)initWithCoordinator:(WebCore::MediaSessionCoordinatorClient*)coordinator |
| { |
| self = [super init]; |
| if (!self) |
| return nil; |
| m_coordinatorClient = coordinator; |
| return self; |
| } |
| |
| - (void)seekSessionToTime:(double)time withCompletion:(void(^)(BOOL))completionHandler |
| { |
| m_coordinatorClient->seekSessionToTime(time, makeBlockPtr(completionHandler)); |
| } |
| |
| - (void)playSessionWithCompletion:(void(^)(BOOL))completionHandler |
| { |
| m_coordinatorClient->playSession({ }, std::optional<MonotonicTime>(), makeBlockPtr(completionHandler)); |
| } |
| |
| - (void)pauseSessionWithCompletion:(void(^)(BOOL))completionHandler |
| { |
| m_coordinatorClient->pauseSession(makeBlockPtr(completionHandler)); |
| } |
| |
| - (void)setSessionTrack:(NSString*)trackIdentifier withCompletion:(void(^)(BOOL))completionHandler |
| { |
| m_coordinatorClient->setSessionTrack(trackIdentifier, makeBlockPtr(completionHandler)); |
| } |
| |
| - (void)coordinatorStateChanged:(_WKMediaSessionCoordinatorState)state |
| { |
| static_assert(static_cast<size_t>(WebCore::MediaSessionCoordinatorState::Waiting) == static_cast<size_t>(WKMediaSessionCoordinatorStateWaiting), "WKMediaSessionCoordinatorStateWaiting does not match WebKit value"); |
| static_assert(static_cast<size_t>(WebCore::MediaSessionCoordinatorState::Joined) == static_cast<size_t>(WKMediaSessionCoordinatorStateJoined), "WKMediaSessionCoordinatorStateJoined does not match WebKit value"); |
| static_assert(static_cast<size_t>(WebCore::MediaSessionCoordinatorState::Closed) == static_cast<size_t>(WKMediaSessionCoordinatorStateClosed), "WKMediaSessionCoordinatorStateClosed does not match WebKit value"); |
| |
| m_coordinatorClient->coordinatorStateChanged(static_cast<WebCore::MediaSessionCoordinatorState>(state)); |
| } |
| |
| @end |
| #endif |