Source/WebCore/ChangeLog

112017-06-14 Jer Noble <jer.noble@apple.com>
22
 3 [WebRTC] Removing a MediaStreamTrack from a MediaStream reports no recording to WebKit clients
 4 https://bugs.webkit.org/show_bug.cgi?id=173398
 5 <rdar://problem/32592961>
 6
 7 Reviewed by Eric Carlson.
 8
 9 API Test: Tests/WebKit2/MediaStreamTrackDetached.mm
 10
 11 Move the definition of a MediaStream as a MediaProducer from the stream itself to its constituent
 12 MediaStreamTracks. This ensures that, even if a MediaStreamTrack is removed from its stream, the
 13 document (and thus the clients) are notified that media capture is still occurring.
 14
 15 Though MediaStream is no longer a MediaProducer, it still uses the MediaProducer's state concept
 16 to determine when to fire events. However it's mediaState() implementation will be moved into
 17 MediaStreamTrack, and will instead simply bitwise-or together each of it's track's mediaState().
 18
 19 The MediaStream notifies the document that its state has changed asynchronously, so do the same
 20 for MediaStreamTrack (which reduces the number of calls to the client when changes all occur
 21 during a single run loop).
 22
 23 Because the MediaStreamTrackPrivate may be started externally (not by the MediaStreamTrack directly),
 24 add a new client method that notifies observers when the track has been started, and the
 25 MediaStreamTrack will use this notification to update the document with it's new mediaState().
 26
 27 * Modules/mediastream/MediaStream.cpp:
 28 (WebCore::MediaStream::MediaStream):
 29 (WebCore::MediaStream::~MediaStream):
 30 (WebCore::MediaStream::mediaState):
 31 (WebCore::MediaStream::statusDidChange):
 32 (WebCore::MediaStream::characteristicsChanged):
 33 (WebCore::MediaStream::pageMutedStateDidChange): Deleted.
 34 * Modules/mediastream/MediaStream.h:
 35 * Modules/mediastream/MediaStreamTrack.cpp:
 36 (WebCore::MediaStreamTrack::MediaStreamTrack):
 37 (WebCore::MediaStreamTrack::~MediaStreamTrack):
 38 (WebCore::MediaStreamTrack::pageMutedStateDidChange):
 39 (WebCore::MediaStreamTrack::mediaState):
 40 (WebCore::MediaStreamTrack::trackStarted):
 41 (WebCore::MediaStreamTrack::configureTrackRendering):
 42 (WebCore::MediaStreamTrack::stop):
 43 (WebCore::MediaStreamTrack::document):
 44 * Modules/mediastream/MediaStreamTrack.h:
 45 (WebCore::MediaStreamTrack::source):
 46 * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.h:
 47 * platform/mediastream/MediaStreamPrivate.cpp:
 48 (WebCore::MediaStreamPrivate::trackStarted):
 49 * platform/mediastream/MediaStreamPrivate.h:
 50 * platform/mediastream/MediaStreamTrackPrivate.cpp:
 51 (WebCore::MediaStreamTrackPrivate::sourceStarted):
 52 * platform/mediastream/MediaStreamTrackPrivate.h:
 53 * platform/mediastream/RealtimeMediaSource.cpp:
 54 (WebCore::RealtimeMediaSource::start):
 55 * platform/mediastream/RealtimeMediaSource.h:
 56
 572017-06-14 Jer Noble <jer.noble@apple.com>
 58
359 Video flashes black when switching back to a tab https://www.apple.com/homepod/
460 https://bugs.webkit.org/show_bug.cgi?id=173377
561

Source/WebCore/Modules/mediastream/MediaStream.cpp

@@MediaStream::MediaStream(ScriptExecutionContext& context, const MediaStreamTrack
9090 setIsActive(m_private->active());
9191 m_private->addObserver(*this);
9292 MediaStreamRegistry::shared().registerStream(*this);
93  document()->addAudioProducer(this);
9493 suspendIfNeeded();
9594}
9695

@@MediaStream::MediaStream(ScriptExecutionContext& context, Ref<MediaStreamPrivate
111110 track->addObserver(*this);
112111 m_trackSet.add(track->id(), WTFMove(track));
113112 }
114  document()->addAudioProducer(this);
115113 suspendIfNeeded();
116114}
117115

@@MediaStream::~MediaStream()
125123 for (auto& track : m_trackSet.values())
126124 track->removeObserver(*this);
127125 if (Document* document = this->document()) {
128  document->removeAudioProducer(this);
129126 if (m_isWaitingUntilMediaCanStart)
130127 document->removeMediaCanStartListener(this);
131128 }

@@void MediaStream::endCaptureTracks()
322319 }
323320}
324321
325 void MediaStream::pageMutedStateDidChange()
326 {
327  if (!m_isActive)
328  return;
329 
330  Document* document = this->document();
331  if (!document)
332  return;
333 
334  m_private->setCaptureTracksMuted(document->page()->isMediaCaptureMuted());
335 }
336 
337322MediaProducer::MediaStateFlags MediaStream::mediaState() const
338323{
339  MediaStateFlags state = IsNotPlaying;
 324 MediaProducer::MediaStateFlags state = MediaProducer::IsNotPlaying;
340325
341326 if (!m_isActive || !document() || !document()->page())
342327 return state;
343328
344  bool pageCaptureMuted = document()->page()->isMediaCaptureMuted();
345  for (const auto& track : m_trackSet.values()) {
346  if (!track->isCaptureTrack() || track->ended())
347  continue;
348 
349  if (track->source().type() == RealtimeMediaSource::Type::Audio) {
350  if (track->source().interrupted() && !pageCaptureMuted)
351  state |= HasInterruptedAudioCaptureDevice;
352  else if (track->muted())
353  state |= HasMutedAudioCaptureDevice;
354  else if (m_isProducingData && m_private->isProducingData()) {
355  state |= HasActiveAudioCaptureDevice;
356  ASSERT(!track->source().interrupted());
357  ASSERT(!track->muted());
358  }
359  } else {
360  if (track->source().interrupted() && !pageCaptureMuted)
361  state |= HasInterruptedVideoCaptureDevice;
362  else if (track->muted())
363  state |= HasMutedVideoCaptureDevice;
364  else if (m_isProducingData && m_private->isProducingData()) {
365  state |= HasActiveVideoCaptureDevice;
366  ASSERT(!track->source().interrupted());
367  ASSERT(!track->muted());
368  }
369  }
370  }
 329 for (const auto& track : m_trackSet.values())
 330 state |= track->mediaState();
371331
372332 return state;
373333}

@@void MediaStream::statusDidChange()
379339 if (Document* document = this->document()) {
380340 if (m_isActive)
381341 document->setHasActiveMediaStreamTrack();
382  document->updateIsPlayingMedia();
383342 }
384343}
385344
386345void MediaStream::characteristicsChanged()
387346{
388  MediaStateFlags state = mediaState();
 347 auto state = mediaState();
389348 if (m_state != state) {
390349 m_state = state;
391350 statusDidChange();

Source/WebCore/Modules/mediastream/MediaStream.h

@@class MediaStream final
5454 , public ActiveDOMObject
5555 , public MediaStreamTrack::Observer
5656 , public MediaStreamPrivate::Observer
57  , private MediaProducer
5857 , private MediaCanStartListener
5958 , private PlatformMediaSessionClient
6059 , public RefCounted<MediaStream> {

@@private:
136135 void didRemoveTrack(MediaStreamTrackPrivate&) final;
137136 void characteristicsChanged() final;
138137
139  // MediaProducer
140  void pageMutedStateDidChange() final;
141  MediaProducer::MediaStateFlags mediaState() const final;
 138 MediaProducer::MediaStateFlags mediaState() const;
142139
143140 // MediaCanStartListener
144141 void mediaCanStart(Document&) final;

@@private:
180177 Vector<Observer*> m_observers;
181178 std::unique_ptr<PlatformMediaSession> m_mediaSession;
182179
183  MediaStateFlags m_state { IsNotPlaying };
 180 MediaProducer::MediaStateFlags m_state { MediaProducer::IsNotPlaying };
184181
185182 bool m_isActive { false };
186183 bool m_isProducingData { false };

Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp

3030
3131#if ENABLE(MEDIA_STREAM)
3232
 33#include "Document.h"
3334#include "Event.h"
3435#include "EventNames.h"
3536#include "JSOverconstrainedError.h"

3839#include "MediaStreamPrivate.h"
3940#include "NotImplemented.h"
4041#include "OverconstrainedError.h"
 42#include "Page.h"
4143#include "ScriptExecutionContext.h"
4244#include <wtf/NeverDestroyed.h>
4345

@@MediaStreamTrack::MediaStreamTrack(ScriptExecutionContext& context, Ref<MediaStr
5254 : ActiveDOMObject(&context)
5355 , m_private(WTFMove(privateTrack))
5456 , m_weakPtrFactory(this)
 57 , m_taskQueue(context)
5558{
5659 suspendIfNeeded();
5760
5861 m_private->addObserver(*this);
 62
 63 if (auto document = this->document())
 64 document->addAudioProducer(this);
5965}
6066
6167MediaStreamTrack::~MediaStreamTrack()
6268{
6369 m_private->removeObserver(*this);
 70
 71 if (auto document = this->document())
 72 document->removeAudioProducer(this);
6473}
6574
6675const AtomicString& MediaStreamTrack::kind() const

@@void MediaStreamTrack::removeObserver(Observer& observer)
285294 m_observers.removeFirst(&observer);
286295}
287296
 297void MediaStreamTrack::pageMutedStateDidChange()
 298{
 299 if (m_ended || !isCaptureTrack())
 300 return;
 301
 302 Document* document = this->document();
 303 if (!document || !document->page())
 304 return;
 305
 306 m_private->setMuted(document->page()->isMediaCaptureMuted());
 307}
 308
 309MediaProducer::MediaStateFlags MediaStreamTrack::mediaState() const
 310{
 311 if (m_ended || !isCaptureTrack())
 312 return IsNotPlaying;
 313
 314 Document* document = this->document();
 315 if (!document || !document->page())
 316 return IsNotPlaying;
 317
 318 bool pageCaptureMuted = document->page()->isMediaCaptureMuted();
 319
 320 if (source().type() == RealtimeMediaSource::Type::Audio) {
 321 if (source().interrupted() && !pageCaptureMuted)
 322 return HasInterruptedAudioCaptureDevice;
 323 if (muted())
 324 return HasMutedAudioCaptureDevice;
 325 if (m_private->isProducingData())
 326 return HasActiveAudioCaptureDevice;
 327 } else {
 328 if (source().interrupted() && !pageCaptureMuted)
 329 return HasInterruptedVideoCaptureDevice;
 330 if (muted())
 331 return HasMutedVideoCaptureDevice;
 332 if (m_private->isProducingData())
 333 return HasActiveVideoCaptureDevice;
 334 }
 335
 336 return IsNotPlaying;
 337}
 338
 339void MediaStreamTrack::trackStarted(MediaStreamTrackPrivate&)
 340{
 341 configureTrackRendering();
 342}
 343
288344void MediaStreamTrack::trackEnded(MediaStreamTrackPrivate&)
289345{
290346 // http://w3c.github.io/mediacapture-main/#life-cycle

@@void MediaStreamTrack::trackEnabledChanged(MediaStreamTrackPrivate&)
332388
333389void MediaStreamTrack::configureTrackRendering()
334390{
 391 m_taskQueue.enqueueTask([this] {
 392 if (auto document = this->document())
 393 document->updateIsPlayingMedia();
 394 });
 395
335396 // 4.3.1
336397 // ... media from the source only flows when a MediaStreamTrack object is both unmuted and enabled
337398}

@@void MediaStreamTrack::configureTrackRendering()
339400void MediaStreamTrack::stop()
340401{
341402 stopTrack();
 403 m_taskQueue.close();
342404}
343405
344406const char* MediaStreamTrack::activeDOMObjectName() const

@@AudioSourceProvider* MediaStreamTrack::audioSourceProvider()
361423 return m_private->audioSourceProvider();
362424}
363425
 426Document* MediaStreamTrack::document() const
 427{
 428 return downcast<Document>(scriptExecutionContext());
 429}
 430
364431} // namespace WebCore
365432
366433#endif // ENABLE(MEDIA_STREAM)

Source/WebCore/Modules/mediastream/MediaStreamTrack.h

3232#include "ActiveDOMObject.h"
3333#include "DoubleRange.h"
3434#include "EventTarget.h"
 35#include "GenericTaskQueue.h"
3536#include "JSDOMPromiseDeferred.h"
3637#include "LongRange.h"
 38#include "MediaProducer.h"
3739#include "MediaStreamTrackPrivate.h"
3840#include "MediaTrackConstraints.h"
3941
4042namespace WebCore {
4143
4244class AudioSourceProvider;
 45class Document;
4346
4447struct MediaTrackConstraints;
4548
46 class MediaStreamTrack : public RefCounted<MediaStreamTrack>, public ActiveDOMObject, public EventTargetWithInlineData, private MediaStreamTrackPrivate::Observer {
 49class MediaStreamTrack :
 50 public RefCounted<MediaStreamTrack>,
 51 public ActiveDOMObject,
 52 public EventTargetWithInlineData,
 53 private MediaProducer,
 54 private MediaStreamTrackPrivate::Observer {
4755public:
4856 class Observer {
4957 public:

@@public:
110118 const MediaTrackConstraints& getConstraints() const { return m_constraints; }
111119 void applyConstraints(const std::optional<MediaTrackConstraints>&, DOMPromiseDeferred<void>&&);
112120
113  RealtimeMediaSource& source() { return m_private->source(); }
 121 RealtimeMediaSource& source() const { return m_private->source(); }
114122 MediaStreamTrackPrivate& privateTrack() { return m_private.get(); }
115123
116124 AudioSourceProvider* audioSourceProvider();
117125
 126 // MediaProducer
 127 void pageMutedStateDidChange() final;
 128 MediaProducer::MediaStateFlags mediaState() const final;
 129
118130 void addObserver(Observer&);
119131 void removeObserver(Observer&);
120132

@@private:
132144
133145 void configureTrackRendering();
134146
 147 Document* document() const;
 148
135149 // ActiveDOMObject API.
136150 void stop() final;
137151 const char* activeDOMObjectName() const final;

@@private:
144158 ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
145159
146160 // MediaStreamTrackPrivate::Observer
 161 void trackStarted(MediaStreamTrackPrivate&) final;
147162 void trackEnded(MediaStreamTrackPrivate&) final;
148163 void trackMutedChanged(MediaStreamTrackPrivate&) final;
149164 void trackSettingsChanged(MediaStreamTrackPrivate&) final;

@@private:
157172 MediaTrackConstraints m_constraints;
158173 std::optional<DOMPromiseDeferred<void>> m_promise;
159174 WeakPtrFactory<MediaStreamTrack> m_weakPtrFactory;
 175 GenericTaskQueue<ScriptExecutionContext> m_taskQueue;
160176
161177 bool m_ended { false };
162178};

Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.h

@@private:
201201 void didRemoveTrack(MediaStreamTrackPrivate&) override;
202202
203203 // MediaStreamPrivateTrack::Observer
 204 void trackStarted(MediaStreamTrackPrivate&) override { };
204205 void trackEnded(MediaStreamTrackPrivate&) override { };
205206 void trackMutedChanged(MediaStreamTrackPrivate&) override { };
206207 void trackSettingsChanged(MediaStreamTrackPrivate&) override { };

Source/WebCore/platform/mediastream/MediaStreamPrivate.cpp

@@void MediaStreamPrivate::trackEnabledChanged(MediaStreamTrackPrivate&)
281281 });
282282}
283283
 284void MediaStreamPrivate::trackStarted(MediaStreamTrackPrivate&)
 285{
 286 scheduleDeferredTask([this] {
 287 characteristicsChanged();
 288 });
 289}
 290
284291void MediaStreamPrivate::trackEnded(MediaStreamTrackPrivate&)
285292{
286293 scheduleDeferredTask([this] {

Source/WebCore/platform/mediastream/MediaStreamPrivate.h

@@private:
125125 MediaStreamPrivate(const MediaStreamTrackPrivateVector&, String&&);
126126
127127 // MediaStreamTrackPrivate::Observer
 128 void trackStarted(MediaStreamTrackPrivate&) override;
128129 void trackEnded(MediaStreamTrackPrivate&) override;
129130 void trackMutedChanged(MediaStreamTrackPrivate&) override;
130131 void trackSettingsChanged(MediaStreamTrackPrivate&) override;

Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.cpp

@@AudioSourceProvider* MediaStreamTrackPrivate::audioSourceProvider()
151151 return m_source->audioSourceProvider();
152152}
153153
 154void MediaStreamTrackPrivate::sourceStarted()
 155{
 156 for (auto& observer : m_observers)
 157 observer->trackStarted(*this);
 158}
 159
154160void MediaStreamTrackPrivate::sourceStopped()
155161{
156162 if (m_isEnded)

Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.h

@@public:
4343 public:
4444 virtual ~Observer() { }
4545
 46 virtual void trackStarted(MediaStreamTrackPrivate&) { };
4647 virtual void trackEnded(MediaStreamTrackPrivate&) = 0;
4748 virtual void trackMutedChanged(MediaStreamTrackPrivate&) = 0;
4849 virtual void trackSettingsChanged(MediaStreamTrackPrivate&) = 0;

@@private:
102103 MediaStreamTrackPrivate(Ref<RealtimeMediaSource>&&, String&& id);
103104
104105 // RealtimeMediaSourceObserver
 106 void sourceStarted() final;
105107 void sourceStopped() final;
106108 void sourceMutedChanged() final;
107109 void sourceEnabledChanged() final;

Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp

@@void RealtimeMediaSource::start()
162162
163163 m_isProducingData = true;
164164 startProducingData();
 165
 166 for (Observer& observer : m_observers)
 167 observer.sourceStarted();
165168}
166169
167170void RealtimeMediaSource::stop()

Source/WebCore/platform/mediastream/RealtimeMediaSource.h

@@public:
7070 virtual ~Observer() { }
7171
7272 // Source state changes.
 73 virtual void sourceStarted() { }
7374 virtual void sourceStopped() { }
7475 virtual void sourceMutedChanged() { }
7576 virtual void sourceEnabledChanged() { }

Tools/ChangeLog

 12017-06-14 Jer Noble <jer.noble@apple.com>
 2
 3 [WebRTC] Removing a MediaStreamTrack from a MediaStream reports no recording to WebKit clients
 4 https://bugs.webkit.org/show_bug.cgi?id=173398
 5 <rdar://problem/32592961>
 6
 7 Reviewed by Eric Carlson.
 8
 9 * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
 10 * TestWebKitAPI/Tests/WebKit2/MediaStreamTrackDetached.mm: Added.
 11 (-[MediaStreamTrackDetachedUIDelegate _webView:requestUserMediaAuthorizationForDevices:url:mainFrameURL:decisionHandler:]):
 12 (-[MediaStreamTrackDetachedUIDelegate _webView:checkUserMediaPermissionForURL:mainFrameURL:frameIdentifier:decisionHandler:]):
 13 (-[MediaStreamTrackDetachedUIDelegate _webView:mediaCaptureStateDidChange:]):
 14 (TestWebKitAPI::TEST):
 15 * TestWebKitAPI/Tests/WebKit2/mediastreamtrack-detached.html: Added.
 16
1172017-06-15 Carlos Garcia Campos <cgarcia@igalia.com>
218
319 [GTK] Add API to allow overriding popup menus

Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj

601601 CDC8E4951BC6F10800594FEC /* video-with-audio.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E48A1BC5C96200594FEC /* video-with-audio.mp4 */; };
602602 CDC8E4961BC6F10800594FEC /* video-without-audio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E48B1BC5C96200594FEC /* video-without-audio.html */; };
603603 CDC8E4971BC6F10800594FEC /* video-without-audio.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E48C1BC5C96200594FEC /* video-without-audio.mp4 */; };
 604 CDC9442E1EF1FC080059C3C4 /* MediaStreamTrackDetached.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDC9442C1EF1FC080059C3C4 /* MediaStreamTrackDetached.mm */; };
 605 CDC9442F1EF205D60059C3C4 /* mediastreamtrack-detached.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC9442B1EF1FBD20059C3C4 /* mediastreamtrack-detached.html */; };
604606 CDCFA7AA1E45183200C2433D /* SampleMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDCFA7A91E45122F00C2433D /* SampleMap.cpp */; };
605607 CDE195B51CFE0B880053D256 /* FullscreenTopContentInset.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDE195B21CFE0ADE0053D256 /* FullscreenTopContentInset.html */; };
606608 CE06DF9B1E1851F200E570C9 /* SecurityOrigin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE06DF9A1E1851F200E570C9 /* SecurityOrigin.cpp */; };

709711 dstPath = TestWebKitAPI.resources;
710712 dstSubfolderSpec = 7;
711713 files = (
 714 CDC9442F1EF205D60059C3C4 /* mediastreamtrack-detached.html in Copy Resources */,
712715 F46849C01EEF5EF300B937FE /* rich-and-plain-text.html in Copy Resources */,
713716 93E2D2761ED7D53200FA76F6 /* offscreen-iframe-of-media-document.html in Copy Resources */,
714717 F46A095A1ED8A6E600D4AA55 /* apple.gif in Copy Resources */,

15231526 CDC8E48A1BC5C96200594FEC /* video-with-audio.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "video-with-audio.mp4"; sourceTree = "<group>"; };
15241527 CDC8E48B1BC5C96200594FEC /* video-without-audio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "video-without-audio.html"; sourceTree = "<group>"; };
15251528 CDC8E48C1BC5C96200594FEC /* video-without-audio.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "video-without-audio.mp4"; sourceTree = "<group>"; };
 1529 CDC9442B1EF1FBD20059C3C4 /* mediastreamtrack-detached.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "mediastreamtrack-detached.html"; sourceTree = "<group>"; };
 1530 CDC9442C1EF1FC080059C3C4 /* MediaStreamTrackDetached.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MediaStreamTrackDetached.mm; sourceTree = "<group>"; };
15261531 CDCFA7A91E45122F00C2433D /* SampleMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleMap.cpp; sourceTree = "<group>"; };
15271532 CDE195B21CFE0ADE0053D256 /* FullscreenTopContentInset.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = FullscreenTopContentInset.html; sourceTree = "<group>"; };
15281533 CDE195B31CFE0ADE0053D256 /* FullscreenTopContentInset.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FullscreenTopContentInset.mm; sourceTree = "<group>"; };

21632168 33DC8910141953A300747EF7 /* LoadCanceledNoServerRedirectCallback.cpp */,
21642169 33DC89131419579F00747EF7 /* LoadCanceledNoServerRedirectCallback_Bundle.cpp */,
21652170 8AA28C1916D2FA7B002FF4DB /* LoadPageOnCrash.cpp */,
 2171 CDC9442C1EF1FC080059C3C4 /* MediaStreamTrackDetached.mm */,
21662172 7A5623101AD5AF3E0096B920 /* MenuTypesForMouseEvents.cpp */,
21672173 51CB4AD71B3A079C00C1B1C6 /* ModalAlertsSPI.cpp */,
21682174 33BE5AF4137B5A6C00705813 /* MouseMoveAfterCrash.cpp */,

23472353 930AD401150698B30067970F /* lots-of-text.html */,
23482354 5797FE321EB15A8900B2F4A0 /* navigation-client-default-crypto.html */,
23492355 AD57AC1D1DA7463800FF1BDE /* many-iframes.html */,
 2356 CDC9442B1EF1FBD20059C3C4 /* mediastreamtrack-detached.html */,
23502357 51CD1C711B38D48400142CA5 /* modal-alerts-in-new-about-blank-window.html */,
23512358 7A1458FB1AD5C03500E06772 /* mouse-button-listener.html */,
23522359 33E79E05137B5FCE00E32D99 /* mouse-move-listener.html */,

29072914 51D1249B1E785425002B2820 /* CookieManager.cpp in Sources */,
29082915 7CCE7EAC1A411A3400447C4C /* Counters.cpp in Sources */,
29092916 7AEAD47F1E20116C00416EFE /* CrossPartitionFileSchemeAccess.mm in Sources */,
 2917 CDC9442E1EF1FC080059C3C4 /* MediaStreamTrackDetached.mm in Sources */,
29102918 7CCE7EDB1A411A9200447C4C /* CSSParser.cpp in Sources */,
29112919 7CCE7F291A411B1000447C4C /* CustomProtocolsInvalidScheme.mm in Sources */,
29122920 7CCE7F2A1A411B1000447C4C /* CustomProtocolsSyncXHRTest.mm in Sources */,

Tools/TestWebKitAPI/Tests/WebKit2/MediaStreamTrackDetached.mm

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26#import "config.h"
 27
 28#if WK_API_ENABLED
 29
 30#if ENABLE(MEDIA_STREAM)
 31
 32#import "PlatformUtilities.h"
 33#import "Test.h"
 34#import "TestWKWebView.h"
 35#import <WebKit/WKPreferencesPrivate.h>
 36#import <WebKit/WKUIDelegatePrivate.h>
 37#import <WebKit/WKWebViewConfiguration.h>
 38#import <WebKit/_WKProcessPoolConfiguration.h>
 39
 40static bool hasRecievedCorrectCaptureState = false;
 41
 42@interface MediaStreamTrackDetachedUIDelegate : NSObject<WKUIDelegate>
 43- (void)_webView:(WKWebView *)webView requestUserMediaAuthorizationForDevices:(_WKCaptureDevices)devices url:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL decisionHandler:(void (^)(BOOL authorized))decisionHandler;
 44- (void)_webView:(WKWebView *)webView checkUserMediaPermissionForURL:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL frameIdentifier:(NSUInteger)frameIdentifier decisionHandler:(void (^)(NSString *salt, BOOL authorized))decisionHandler;
 45- (void)_webView:(WKWebView *)webView mediaCaptureStateDidChange:(_WKMediaCaptureState)state;
 46@end
 47
 48@implementation MediaStreamTrackDetachedUIDelegate
 49- (void)_webView:(WKWebView *)webView requestUserMediaAuthorizationForDevices:(_WKCaptureDevices)devices url:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL decisionHandler:(void (^)(BOOL authorized))decisionHandler
 50{
 51 decisionHandler(YES);
 52}
 53
 54- (void)_webView:(WKWebView *)webView checkUserMediaPermissionForURL:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL frameIdentifier:(NSUInteger)frameIdentifier decisionHandler:(void (^)(NSString *salt, BOOL authorized))decisionHandler
 55{
 56 decisionHandler(@"0x987654321", YES);
 57}
 58- (void)_webView:(WKWebView *)webView mediaCaptureStateDidChange:(_WKMediaCaptureState)state
 59{
 60 if (state == _WKMediaCaptureStateActiveMicrophone)
 61 hasRecievedCorrectCaptureState = true;
 62}
 63@end
 64
 65namespace TestWebKitAPI {
 66
 67TEST(WebKit2, MediaStreamTrackDetached)
 68{
 69 auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
 70 auto processPoolConfig = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
 71 auto preferences = [configuration preferences];
 72 preferences._mediaCaptureRequiresSecureConnection = NO;
 73 preferences._mediaDevicesEnabled = YES;
 74 auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get() processPoolConfiguration:processPoolConfig.get()]);
 75 auto delegate = adoptNS([[MediaStreamTrackDetachedUIDelegate alloc] init]);
 76 webView.get().UIDelegate = delegate.get();
 77
 78 hasRecievedCorrectCaptureState = false;
 79 [webView loadTestPageNamed:@"mediastreamtrack-detached"];
 80
 81 TestWebKitAPI::Util::run(&hasRecievedCorrectCaptureState);
 82
 83
 84}
 85
 86} // namespace TestWebKitAPI
 87
 88#endif // ENABLE(MEDIA_STREAM)
 89
 90#endif // WK_API_ENABLED

Tools/TestWebKitAPI/Tests/WebKit2/mediastreamtrack-detached.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<script>
 5var audioTrack;
 6
 7function go() {
 8 var constraints = { audio: true, video: true};
 9 navigator.mediaDevices.getUserMedia(constraints).then(gotUserMedia);
 10}
 11
 12function gotUserMedia(stream) {
 13 audioTrack = stream.getAudioTracks()[0];
 14 stream.removeTrack(audioTrack);
 15
 16 stream.getTracks().forEach(track => { track.stop(); });
 17}
 18</script>
 19</head>
 20<body onload="go()">
 21 <div>This tests the behavior of the media capture indicator when a live MediaStreamTrack is removed from a MediaStream, and all the other tracks are stopped. The page should request access to the camera and microphone, and then display a audio-capture icon. If the page displays a camera icon or no capture icon at all, the test has failed.</div>
 22</body>
 23</html>