Add experimental TextTrackCue API
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Nov 2019 22:49:54 +0000 (22:49 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Nov 2019 22:49:54 +0000 (22:49 +0000)
https://bugs.webkit.org/show_bug.cgi?id=203649
<rdar://problem/55675172>

Reviewed by Jer Noble.

Source/WebCore:

Tests: media/track/texttrackcue/texttrackcue-addcue.html
       media/track/texttrackcue/texttrackcue-constructor.html
       media/track/texttrackcue/texttrackcue-displaycue.html

* Modules/mediacontrols/MediaControlsHost.cpp:
(WebCore::MediaControlsHost::updateCaptionDisplaySizes):
* Modules/mediacontrols/MediaControlsHost.h:
* bindings/js/JSTextTrackCueCustom.cpp:
(WebCore::toJSNewlyCreated):
* bindings/js/WebCoreBuiltinNames.h:
* bindings/scripts/CodeGeneratorJS.pm:
(NeedsRuntimeCheck):
(GenerateRuntimeEnableConditionalString):
* bindings/scripts/IDLAttributes.json:
* bindings/scripts/preprocess-idls.pl:
(GenerateConstructorAttributes):
* dom/DocumentFragment.idl:
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::updateRenderer):
(WebCore::HTMLMediaElement::updateActiveTextTrackCues):
(WebCore::HTMLMediaElement::textTrackRemoveCue):
(WebCore::HTMLMediaElement::configureTextTrackDisplay):
(WebCore::HTMLMediaElement::captionPreferencesChanged):
* html/shadow/MediaControlElements.cpp:
(WebCore::MediaControlTextTrackContainerElement::updateDisplay):
(WebCore::MediaControlTextTrackContainerElement::processActiveVTTCue):
(WebCore::MediaControlTextTrackContainerElement::updateActiveCuesFontSize):
(WebCore::MediaControlTextTrackContainerElement::updateTimerFired):
(WebCore::MediaControlTextTrackContainerElement::updateStyleForTextTrackRepresentation):
* html/shadow/MediaControlElements.h:
* html/track/TextTrack.cpp:
(WebCore::TextTrack::setMode):
* html/track/TextTrackCue.cpp:
(WebCore::TextTrackCue::cueBoxShadowPseudoId):
(WebCore::TextTrackCue::cueBackdropShadowPseudoId):
(WebCore::cueAttributName):
(WebCore::cueBackgroundAttributName):
(WebCore::TextTrackCueBox::TextTrackCueBox):
(WebCore::TextTrackCueBox::getCue const):
(WebCore::isLegalNode):
(WebCore::invalidNodeException):
(WebCore::checkForInvalidNodeTypes):
(WebCore::tagPseudoObjects):
(WebCore::removePseudoAttributes):
(WebCore::TextTrackCue::create):
(WebCore::TextTrackCue::TextTrackCue):
(WebCore::TextTrackCue::didChange):
(WebCore::TextTrackCue::setIsActive):
(WebCore::TextTrackCue::toJSON const):
(WebCore::TextTrackCue::debugString const):
(WebCore::TextTrackCue::getCueAsHTML):
(WebCore::TextTrackCue::isRenderable const):
(WebCore::TextTrackCue::getDisplayTree):
(WebCore::TextTrackCue::removeDisplayTree):
(WebCore::TextTrackCue::setFontSize):
(WebCore::TextTrackCue::rebuildDisplayTree):
* html/track/TextTrackCue.h:
(WebCore::TextTrackCueBox::create):
(WebCore::TextTrackCueBox::applyCSSProperties):
(WebCore::TextTrackCueBox::~TextTrackCueBox):
(WebCore::TextTrackCue::cueType const):
(WebCore::TextTrackCue::recalculateStyles):
(WebCore::TextTrackCue::updateDisplayTree):
(WebCore::TextTrackCue::isRenderable const): Deleted.
* html/track/TextTrackCue.idl:
* html/track/TextTrackCueGeneric.cpp:
(WebCore::TextTrackCueGeneric::isEqual const):
(WebCore::TextTrackCueGeneric::isOrderedBefore const):
(WebCore::TextTrackCueGeneric::isPositionedAbove const):
* html/track/TextTrackCueGeneric.h:
(isType):
* html/track/VTTCue.cpp:
(WebCore::VTTCueBox::VTTCueBox):
(WebCore::VTTCueBox::applyCSSProperties):
(WebCore::VTTCue::getDisplayTree):
(WebCore::VTTCue::removeDisplayTree):
(WebCore::VTTCue::setFontSize):
(WebCore::toVTTCue):
(WebCore::VTTCueBox::create): Deleted.
(WebCore::VTTCueBox::getCue const): Deleted.
(WebCore::VTTCueBox::vttCueBoxShadowPseudoId): Deleted.
(WebCore::VTTCue::cueBackdropShadowPseudoId): Deleted.
* html/track/VTTCue.h:
(WebCore::VTTCueBox::create):
(isType):
* html/track/VTTRegion.cpp:
(WebCore::VTTRegion::appendTextTrackCueBox):
(WebCore::VTTRegion::getDisplayTree):
(WebCore::VTTRegion::prepareRegionDisplayTree):
* html/track/VTTRegion.h:
* page/CaptionUserPreferencesMediaAF.cpp:
(WebCore::CaptionUserPreferencesMediaAF::captionsStyleSheetOverride const):
* page/Settings.yaml:
* rendering/RenderVTTCue.cpp:
(WebCore::RenderVTTCue::RenderVTTCue):

Source/WebKit:

* Shared/WebPreferences.yaml:

LayoutTests:

* media/track/texttrackcue/texttrackcue-addcue-expected.txt: Added.
* media/track/texttrackcue/texttrackcue-addcue.html: Added.
* media/track/texttrackcue/texttrackcue-constructor-expected.txt: Added.
* media/track/texttrackcue/texttrackcue-constructor.html: Added.
* media/track/texttrackcue/texttrackcue-displaycue-expected.txt: Added.
* media/track/texttrackcue/texttrackcue-displaycue.html: Added.
* platform/ios/TestExpectations:
* platform/mac/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@251950 268f45cc-cd09-0410-ab3c-d52691b4dbfc

45 files changed:
LayoutTests/ChangeLog
LayoutTests/media/track/texttrackcue/texttrackcue-addcue-expected.txt [new file with mode: 0644]
LayoutTests/media/track/texttrackcue/texttrackcue-addcue.html [new file with mode: 0644]
LayoutTests/media/track/texttrackcue/texttrackcue-constructor-expected.txt [new file with mode: 0644]
LayoutTests/media/track/texttrackcue/texttrackcue-constructor.html [new file with mode: 0644]
LayoutTests/media/track/texttrackcue/texttrackcue-displaycue-expected.txt [new file with mode: 0644]
LayoutTests/media/track/texttrackcue/texttrackcue-displaycue.html [new file with mode: 0644]
LayoutTests/platform/ios/TestExpectations
LayoutTests/platform/mac/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediacontrols/MediaControlsHost.cpp
Source/WebCore/Modules/mediacontrols/MediaControlsHost.h
Source/WebCore/bindings/js/JSTextTrackCueCustom.cpp
Source/WebCore/bindings/js/WebCoreBuiltinNames.h
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
Source/WebCore/bindings/scripts/IDLAttributes.json
Source/WebCore/bindings/scripts/preprocess-idls.pl
Source/WebCore/dom/DocumentFragment.idl
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/shadow/MediaControlElements.cpp
Source/WebCore/html/shadow/MediaControlElements.h
Source/WebCore/html/shadow/MediaControls.cpp
Source/WebCore/html/track/TextTrack.cpp
Source/WebCore/html/track/TextTrackCue.cpp
Source/WebCore/html/track/TextTrackCue.h
Source/WebCore/html/track/TextTrackCue.idl
Source/WebCore/html/track/TextTrackCueGeneric.cpp
Source/WebCore/html/track/TextTrackCueGeneric.h
Source/WebCore/html/track/VTTCue.cpp
Source/WebCore/html/track/VTTCue.h
Source/WebCore/html/track/VTTRegion.cpp
Source/WebCore/html/track/VTTRegion.h
Source/WebCore/page/CaptionUserPreferencesMediaAF.cpp
Source/WebCore/page/Settings.yaml
Source/WebCore/rendering/RenderVTTCue.cpp
Source/WebKit/ChangeLog
Source/WebKit/Shared/WebPreferences.yaml
Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp
Source/WebKitLegacy/mac/WebView/WebPreferenceKeysPrivate.h
Source/WebKitLegacy/mac/WebView/WebPreferences.mm
Source/WebKitLegacy/mac/WebView/WebPreferencesPrivate.h
Source/WebKitLegacy/mac/WebView/WebView.mm
Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp
Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
Tools/WebKitTestRunner/InjectedBundle/TestRunner.h

index c3e504f..6a6d11e 100644 (file)
@@ -1,3 +1,20 @@
+2019-11-01  Eric Carlson  <eric.carlson@apple.com>
+
+        Add experimental TextTrackCue API
+        https://bugs.webkit.org/show_bug.cgi?id=203649
+        <rdar://problem/55675172>
+
+        Reviewed by Jer Noble.
+
+        * media/track/texttrackcue/texttrackcue-addcue-expected.txt: Added.
+        * media/track/texttrackcue/texttrackcue-addcue.html: Added.
+        * media/track/texttrackcue/texttrackcue-constructor-expected.txt: Added.
+        * media/track/texttrackcue/texttrackcue-constructor.html: Added.
+        * media/track/texttrackcue/texttrackcue-displaycue-expected.txt: Added.
+        * media/track/texttrackcue/texttrackcue-displaycue.html: Added.
+        * platform/ios/TestExpectations:
+        * platform/mac/TestExpectations:
+
 2019-11-01  Kate Cheney  <katherine_cheney@apple.com>
 
         http/tests/websocket/tests/hybi/handshake-ok-with-legacy-websocket-response-headers.html is flaky
diff --git a/LayoutTests/media/track/texttrackcue/texttrackcue-addcue-expected.txt b/LayoutTests/media/track/texttrackcue/texttrackcue-addcue-expected.txt
new file mode 100644 (file)
index 0000000..bfe1a7e
--- /dev/null
@@ -0,0 +1,6 @@
+
+
+PASS setup 
+PASS track.addCue() adds to track.cues 
+PASS cue order should change when start time is changed 
+
diff --git a/LayoutTests/media/track/texttrackcue/texttrackcue-addcue.html b/LayoutTests/media/track/texttrackcue/texttrackcue-addcue.html
new file mode 100644 (file)
index 0000000..91f4298
--- /dev/null
@@ -0,0 +1,63 @@
+<!doctype html>
+<title>TextTrackCue addcue</title>
+<div></div>
+<script src=../../../resources/testharness.js></script>
+<script src=../../../resources/testharnessreport.js></script>
+<script src=../../media-controls.js></script>
+<script src=../../media-file.js></script>
+<script>
+if (window.internals)
+    internals.settings.setGenericCueAPIEnabled(true);
+
+function createValidCueFragment(text)
+{
+    let cueNode = document.createElement('span');
+    cueNode.setAttribute('cue', '');
+    cueNode.innerText = text;
+
+    let backgroundNode = document.createElement('div');
+    backgroundNode.setAttribute('cuebackground', '');
+    backgroundNode.appendChild(cueNode);
+
+    fragment = document.createDocumentFragment();
+    fragment.appendChild(backgroundNode);
+
+    return fragment;
+}
+
+test(test => {
+    window.video = document.createElement('video');
+    video.controls = true;
+
+    window.track = video.addTextTrack('subtitles');
+    track.default = true;
+    track.mode = 'showing';
+
+    document.body.appendChild(video);
+}, 'setup');
+
+test(test => {
+    let cue1 = new TextTrackCue(0, 1, createValidCueFragment('First cue'));
+    cue1.id = "one";
+    track.addCue(cue1);
+    assert_equals(track.cues.length, 1, 'only one cue');
+
+    let cue2 = new TextTrackCue(1, 2, createValidCueFragment('Second cue'));
+    cue2.id = "two";
+    track.addCue(cue2);
+    assert_equals(track.cues.length, 2, "track.cues.length includes second cue");
+    assert_equals(track.cues[0].id, 'one', 'cue 1 should be first');
+    assert_equals(track.cues[1].id, 'two', 'cue 2 should be second');    
+}, 'track.addCue() adds to track.cues');
+
+test(test => {
+    track.mode = 'showing';
+    track.cues[1].startTime = 0;
+    assert_equals(track.cues[0].id, 'two', 'cue 2 should be first');
+    assert_equals(track.cues[1].id, 'one', 'cue 1 should be second');
+
+    track.cues[0].startTime = 0.5;
+    assert_equals(track.cues[0].id, 'one', 'cue 1 should be first');
+    assert_equals(track.cues[1].id, 'two', 'cue 2 should be second');
+}, 'cue order should change when start time is changed');
+</script>
diff --git a/LayoutTests/media/track/texttrackcue/texttrackcue-constructor-expected.txt b/LayoutTests/media/track/texttrackcue/texttrackcue-constructor-expected.txt
new file mode 100644 (file)
index 0000000..a331344
--- /dev/null
@@ -0,0 +1,8 @@
+
+PASS TextTrackCue should throw "TypeError" if the cue node is not a documentFragment 
+PASS TextTrackCue should throw "InvalidNodeTypeError" if passed an invalid documentFragment 
+PASS TextTrackCue should throw "InvalidNodeTypeError" if documentFragment has an invalid node 
+PASS TextTrackCue should throw "InvalidStateError" if documentFragment doesn't have 'cuebackground' and 'cue' nodes 
+PASS TextTrackCue should allow all valid node types 
+PASS Test TextTrackCue.getCueAsHTML() 
+
diff --git a/LayoutTests/media/track/texttrackcue/texttrackcue-constructor.html b/LayoutTests/media/track/texttrackcue/texttrackcue-constructor.html
new file mode 100644 (file)
index 0000000..4b540e4
--- /dev/null
@@ -0,0 +1,105 @@
+<!doctype html>
+<title>TextTrackCue constructor</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script>
+if (window.internals)
+    internals.settings.setGenericCueAPIEnabled(true);
+
+test( test => {
+    assert_throws(new TypeError, _ => { new TextTrackCue(0, 10, "") });
+    assert_throws(new TypeError, _ => { new TextTrackCue(0, 10, { }) });
+    assert_throws(new TypeError, _ => { new TextTrackCue(0, 10, document.createElement('div')) });
+}, 'TextTrackCue should throw "TypeError" if the cue node is not a documentFragment');
+
+test( test => {
+    assert_throws("InvalidNodeTypeError", _ => { new TextTrackCue(0, 10, document.createDocumentFragment()) });
+}, 'TextTrackCue should throw "InvalidNodeTypeError" if passed an invalid documentFragment');
+
+function createValidCueFragment()
+{
+    let cueNode = document.createElement('span');
+    cueNode.setAttribute('cue', '');
+
+    let backgroundNode = document.createElement('div');
+    backgroundNode.setAttribute('cuebackground', '');
+    backgroundNode.appendChild(cueNode);
+
+    fragment = document.createDocumentFragment();
+    fragment.appendChild(backgroundNode);
+
+    return fragment;
+}
+
+test( test => {
+    let fragment = createValidCueFragment();
+    let validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.appendChild(document.createElement('table'));
+    assert_throws("InvalidNodeTypeError", _ => { new TextTrackCue(0, 10, fragment) });
+
+}, `TextTrackCue should throw "InvalidNodeTypeError" if documentFragment has an invalid node`);
+
+test( test => {
+    let fragment = createValidCueFragment();
+    let validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.removeAttribute('cuebackground')
+    assert_throws("InvalidStateError", _ => { new TextTrackCue(0, 10, fragment) });
+
+    fragment = createValidCueFragment();
+    fragment.firstChild.firstChild.removeAttribute('cue')
+    assert_throws("InvalidStateError", _ => { new TextTrackCue(0, 10, fragment) });
+}, `TextTrackCue should throw "InvalidStateError" if documentFragment doesn't have 'cuebackground' and 'cue' nodes`);
+
+test( test => {
+    let fragment = createValidCueFragment();
+    let validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.appendChild(document.createElement('br'));
+    validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.appendChild(document.createElement('div'));
+    validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.appendChild(document.createElement('img'));
+    validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.appendChild(document.createElement('p'));
+    validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.appendChild(document.createElement('rb'));
+    validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.appendChild(document.createElement('rt'));
+    validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.appendChild(document.createElement('rtc'));
+    validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.appendChild(document.createElement('ruby'));
+    validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.appendChild(document.createElement('span'));
+    validCue = new TextTrackCue(0, 10, fragment);
+
+    fragment.firstChild.appendChild(document.createTextNode('Hello!'));
+    validCue = new TextTrackCue(0, 10, fragment);
+
+}, `TextTrackCue should allow all valid node types`);
+
+
+test( test => {
+    let sourceFragment = createValidCueFragment();
+    let validCue = new TextTrackCue(0, 10, fragment);
+
+    let cueFragment = validCue.getCueAsHTML();
+    assert_true(cueFragment instanceof DocumentFragment);
+
+    assert_true(cueFragment.isEqualNode(sourceFragment));
+
+    assert_not_equals(validCue.getCueAsHTML(), cueFragment, "getCueAsHTML() should return a different fragment each time");
+
+}, `Test TextTrackCue.getCueAsHTML()`);
+
+</script>
diff --git a/LayoutTests/media/track/texttrackcue/texttrackcue-displaycue-expected.txt b/LayoutTests/media/track/texttrackcue/texttrackcue-displaycue-expected.txt
new file mode 100644 (file)
index 0000000..b04e1c5
--- /dev/null
@@ -0,0 +1,4 @@
+
+
+PASS Cues are displayed 
+
diff --git a/LayoutTests/media/track/texttrackcue/texttrackcue-displaycue.html b/LayoutTests/media/track/texttrackcue/texttrackcue-displaycue.html
new file mode 100644 (file)
index 0000000..9fc3c21
--- /dev/null
@@ -0,0 +1,94 @@
+<!doctype html>
+<title>TextTrackCue display cue</title>
+
+<div></div>
+<script src=../../../resources/testharness.js></script>
+<script src=../../../resources/testharnessreport.js></script>
+<script src=../../media-controls.js></script>
+<script src=../../media-file.js></script>
+<script>
+if (window.internals)
+    internals.settings.setGenericCueAPIEnabled(true);
+
+function createValidCueFragment(text)
+{
+    let cueNode = document.createElement('span');
+    cueNode.setAttribute('cue', '');
+    cueNode.innerText = text;
+
+    let backgroundNode = document.createElement('div');
+    backgroundNode.setAttribute('cuebackground', '');
+    backgroundNode.appendChild(cueNode);
+
+    fragment = document.createDocumentFragment();
+    fragment.appendChild(backgroundNode);
+
+    return fragment;
+}
+
+async_test(test => {
+
+    video = document.createElement('video');
+    video.controls = true;
+
+    track = video.addTextTrack('subtitles');
+    track.default = true;
+    track.mode = 'showing';
+
+    document.body.appendChild(video);
+
+    let cue1Text = 'First cue';
+    let cue1 = new TextTrackCue(0, 1.1, createValidCueFragment(cue1Text));
+    cue1.id = "one";
+    track.addCue(cue1);
+    assert_equals(track.cues.length, 1, 'only one cue');
+
+    let cue2Text = 'Second cue';
+    let cue2 = new TextTrackCue(1, 2, createValidCueFragment(cue2Text));
+    cue2.id = "two";
+    track.addCue(cue2);
+    assert_equals(track.cues.length, 2, "track.cues.length includes second cue");
+
+    assert_equals(track.cues[0].id, 'one', 'cue 1 should be first');
+    assert_equals(track.cues[1].id, 'two', 'cue 2 should be second');    
+
+    video.addEventListener('canplay', event => {
+
+        let seekCount = 0;
+        let step = _ => {
+            let cueContainer = mediaControlsElement(internals.shadowRoot(video).firstChild, "-webkit-media-text-track-container");
+            switch (++seekCount) {
+            case 1:
+                assert_equals(track.activeCues.length, 1, "only one active cue");
+                assert_equals(cueContainer.innerText, cue1Text, "cue one is visible");
+                video.currentTime = track.cues[1].startTime;
+                break;
+
+            case 2:
+                assert_equals(track.activeCues.length, 2, "two active cues");
+                assert_equals(cueContainer.innerText, `${cue1Text}\n${cue2Text}`, "cues one and two are visible");
+                video.currentTime = track.cues[1].endTime - .5;
+                break;
+
+            case 3:
+                assert_equals(track.activeCues.length, 1, "only one active cue");
+                assert_equals(cueContainer.innerText, cue2Text, "cue two is visible");
+                video.currentTime = track.cues[1].endTime + 1;
+                break;
+
+            case 4:
+                assert_equals(track.activeCues.length, 0, "no active cues");
+                assert_equals(cueContainer.innerText, '', "no cues are visible");
+                test.done();
+                break;
+            }
+        }
+        video.addEventListener('seeked', test.step_func(step));
+        video.currentTime = track.cues[0].startTime;
+    });
+
+    video.src = findMediaFile('video', '../../content/test');
+
+}, 'Cues are displayed');
+
+</script>
index 7cf6957..4eb1477 100644 (file)
@@ -2494,6 +2494,9 @@ webkit.org/b/160367 media/track/track-remove-crash.html
 # They are also flakily failing on WebKit2, as snapshot sometimes has a black rectangle for video element instead of white one.
 webkit.org/b/131476 media/track/w3c/track/webvtt
 
+# VTTRegion height doesn't adjust when cue font size changes.
+webkit.org/b/203621 media/track/regions-webvtt/vtt-region-display.html [ Pass Failure ]
+
 media/video-object-fit-change.html [ ImageOnlyFailure ]
 media/video-object-fit.html [ ImageOnlyFailure ]
 
index b990e92..8510529 100644 (file)
@@ -857,6 +857,9 @@ webkit.org/b/103926 media/track/opera/track/webvtt/rendering/adhoc/voice_with_ev
 # They are also flakily failing on WebKit2, as snapshot sometimes has a black rectangle for video element instead of white one.
 webkit.org/b/131476 media/track/w3c/track/webvtt [ Skip ]
 
+# VTTRegion height doesn't adjust when cue font size changes.
+webkit.org/b/203621 media/track/regions-webvtt/vtt-region-display.html [ Pass Failure ]
+
 webkit.org/b/67716 media/media-controls-invalid-url.html [ Failure ]
 
 webkit.org/b/75184 media/W3C/video/canPlayType/canPlayType_codecs_order_2.html [ Failure ]
index f76e4a4..93004bd 100644 (file)
@@ -1,3 +1,107 @@
+2019-11-01  Eric Carlson  <eric.carlson@apple.com>
+
+        Add experimental TextTrackCue API
+        https://bugs.webkit.org/show_bug.cgi?id=203649
+        <rdar://problem/55675172>
+
+        Reviewed by Jer Noble.
+
+        Tests: media/track/texttrackcue/texttrackcue-addcue.html
+               media/track/texttrackcue/texttrackcue-constructor.html
+               media/track/texttrackcue/texttrackcue-displaycue.html
+
+        * Modules/mediacontrols/MediaControlsHost.cpp:
+        (WebCore::MediaControlsHost::updateCaptionDisplaySizes):
+        * Modules/mediacontrols/MediaControlsHost.h:
+        * bindings/js/JSTextTrackCueCustom.cpp:
+        (WebCore::toJSNewlyCreated):
+        * bindings/js/WebCoreBuiltinNames.h:
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (NeedsRuntimeCheck):
+        (GenerateRuntimeEnableConditionalString):
+        * bindings/scripts/IDLAttributes.json:
+        * bindings/scripts/preprocess-idls.pl:
+        (GenerateConstructorAttributes):
+        * dom/DocumentFragment.idl:
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::updateRenderer):
+        (WebCore::HTMLMediaElement::updateActiveTextTrackCues):
+        (WebCore::HTMLMediaElement::textTrackRemoveCue):
+        (WebCore::HTMLMediaElement::configureTextTrackDisplay):
+        (WebCore::HTMLMediaElement::captionPreferencesChanged):
+        * html/shadow/MediaControlElements.cpp:
+        (WebCore::MediaControlTextTrackContainerElement::updateDisplay):
+        (WebCore::MediaControlTextTrackContainerElement::processActiveVTTCue):
+        (WebCore::MediaControlTextTrackContainerElement::updateActiveCuesFontSize):
+        (WebCore::MediaControlTextTrackContainerElement::updateTimerFired):
+        (WebCore::MediaControlTextTrackContainerElement::updateStyleForTextTrackRepresentation):
+        * html/shadow/MediaControlElements.h:
+        * html/track/TextTrack.cpp:
+        (WebCore::TextTrack::setMode):
+        * html/track/TextTrackCue.cpp:
+        (WebCore::TextTrackCue::cueBoxShadowPseudoId):
+        (WebCore::TextTrackCue::cueBackdropShadowPseudoId):
+        (WebCore::cueAttributName):
+        (WebCore::cueBackgroundAttributName):
+        (WebCore::TextTrackCueBox::TextTrackCueBox):
+        (WebCore::TextTrackCueBox::getCue const):
+        (WebCore::isLegalNode):
+        (WebCore::invalidNodeException):
+        (WebCore::checkForInvalidNodeTypes):
+        (WebCore::tagPseudoObjects):
+        (WebCore::removePseudoAttributes):
+        (WebCore::TextTrackCue::create):
+        (WebCore::TextTrackCue::TextTrackCue):
+        (WebCore::TextTrackCue::didChange):
+        (WebCore::TextTrackCue::setIsActive):
+        (WebCore::TextTrackCue::toJSON const):
+        (WebCore::TextTrackCue::debugString const):
+        (WebCore::TextTrackCue::getCueAsHTML):
+        (WebCore::TextTrackCue::isRenderable const):
+        (WebCore::TextTrackCue::getDisplayTree):
+        (WebCore::TextTrackCue::removeDisplayTree):
+        (WebCore::TextTrackCue::setFontSize):
+        (WebCore::TextTrackCue::rebuildDisplayTree):
+        * html/track/TextTrackCue.h:
+        (WebCore::TextTrackCueBox::create):
+        (WebCore::TextTrackCueBox::applyCSSProperties):
+        (WebCore::TextTrackCueBox::~TextTrackCueBox):
+        (WebCore::TextTrackCue::cueType const):
+        (WebCore::TextTrackCue::recalculateStyles):
+        (WebCore::TextTrackCue::updateDisplayTree):
+        (WebCore::TextTrackCue::isRenderable const): Deleted.
+        * html/track/TextTrackCue.idl:
+        * html/track/TextTrackCueGeneric.cpp:
+        (WebCore::TextTrackCueGeneric::isEqual const):
+        (WebCore::TextTrackCueGeneric::isOrderedBefore const):
+        (WebCore::TextTrackCueGeneric::isPositionedAbove const):
+        * html/track/TextTrackCueGeneric.h:
+        (isType):
+        * html/track/VTTCue.cpp:
+        (WebCore::VTTCueBox::VTTCueBox):
+        (WebCore::VTTCueBox::applyCSSProperties):
+        (WebCore::VTTCue::getDisplayTree):
+        (WebCore::VTTCue::removeDisplayTree):
+        (WebCore::VTTCue::setFontSize):
+        (WebCore::toVTTCue):
+        (WebCore::VTTCueBox::create): Deleted.
+        (WebCore::VTTCueBox::getCue const): Deleted.
+        (WebCore::VTTCueBox::vttCueBoxShadowPseudoId): Deleted.
+        (WebCore::VTTCue::cueBackdropShadowPseudoId): Deleted.
+        * html/track/VTTCue.h:
+        (WebCore::VTTCueBox::create):
+        (isType):
+        * html/track/VTTRegion.cpp:
+        (WebCore::VTTRegion::appendTextTrackCueBox):
+        (WebCore::VTTRegion::getDisplayTree):
+        (WebCore::VTTRegion::prepareRegionDisplayTree):
+        * html/track/VTTRegion.h:
+        * page/CaptionUserPreferencesMediaAF.cpp:
+        (WebCore::CaptionUserPreferencesMediaAF::captionsStyleSheetOverride const):
+        * page/Settings.yaml:
+        * rendering/RenderVTTCue.cpp:
+        (WebCore::RenderVTTCue::RenderVTTCue):
+
 2019-11-01  Chris Dumez  <cdumez@apple.com>
 
         Port ServiceWorkerContainer to the HTML5 event loop
index 10ff72f..536674a 100644 (file)
@@ -177,10 +177,10 @@ void MediaControlsHost::exitedFullscreen()
         m_textTrackContainer->exitedFullscreen();
 }
 
-void MediaControlsHost::updateCaptionDisplaySizes()
+void MediaControlsHost::updateCaptionDisplaySizes(ForceUpdate force)
 {
     if (m_textTrackContainer)
-        m_textTrackContainer->updateSizes(true);
+        m_textTrackContainer->updateSizes(force == ForceUpdate::Yes ? MediaControlTextTrackContainerElement::ForceUpdate::Yes : MediaControlTextTrackContainerElement::ForceUpdate::No);
 }
     
 bool MediaControlsHost::allowsInlineMediaPlayback() const
index ef5d057..9d260d8 100644 (file)
@@ -72,7 +72,8 @@ public:
     bool shouldForceControlsDisplay() const;
     void setPreparedToReturnVideoLayerToInline(bool);
 
-    void updateCaptionDisplaySizes();
+    enum class ForceUpdate { Yes, No };
+    void updateCaptionDisplaySizes(ForceUpdate force = ForceUpdate::No);
     void enteredFullscreen();
     void exitedFullscreen();
 
index 6ecacee..7529572 100644 (file)
@@ -67,12 +67,14 @@ JSValue toJSNewlyCreated(JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<T
     case TextTrackCue::Data:
         return createWrapper<DataCue>(globalObject, WTFMove(cue));
     case TextTrackCue::WebVTT:
-    case TextTrackCue::Generic:
+    case TextTrackCue::ConvertedToWebVTT:
         return createWrapper<VTTCue>(globalObject, WTFMove(cue));
-    default:
-        ASSERT_NOT_REACHED();
-        return jsNull();
+    case TextTrackCue::Generic:
+        return createWrapper<TextTrackCue>(globalObject, WTFMove(cue));
     }
+
+    ASSERT_NOT_REACHED();
+    return jsNull();
 }
 
 JSValue toJS(JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, TextTrackCue& cue)
index b007dd7..8cea7c8 100644 (file)
@@ -217,6 +217,7 @@ namespace WebCore {
     macro(StaticRange) \
     macro(StylePropertyMapReadOnly) \
     macro(StylePropertyMap) \
+    macro(TextTrackCue) \
     macro(UndoItem) \
     macro(UndoManager) \
     macro(VRDisplay) \
index e0f4605..243bc3b 100644 (file)
@@ -2435,7 +2435,7 @@ sub GenerateDictionaryImplementationContent
                 my $needsRuntimeCheck = NeedsRuntimeCheck($dictionary, $member);
                 my $indent = "";
                 if ($needsRuntimeCheck) {
-                    my $runtimeEnableConditionalString = GenerateRuntimeEnableConditionalString($dictionary, $member, "true");
+                    my $runtimeEnableConditionalString = GenerateRuntimeEnableConditionalString($dictionary, $member, "&globalObject");
                     $result .= "    if (${runtimeEnableConditionalString}) {\n";
                     $indent = "    ";
                 }
@@ -3734,7 +3734,7 @@ sub GenerateRuntimeEnableConditionalString
     my ($interface, $context, $globalObjectIsParam) = @_;
 
     my @conjuncts;
-    my $globalObjectPtr = $globalObjectIsParam ? "&globalObject" : "globalObject()";
+    my $globalObjectPtr = $globalObjectIsParam ? $globalObjectIsParam : "globalObject()";
     
     if ($context->extendedAttributes->{SecureContext}) {
         AddToImplIncludes("ScriptExecutionContext.h");
@@ -3823,6 +3823,15 @@ sub GenerateRuntimeEnableConditionalString
         push(@conjuncts,  "${name}::enabledForContext(" . $contextRef . ")");
     }
 
+    if ($context->extendedAttributes->{ConstructorEnabledBySetting}) {
+        assert("Must specify value for ConstructorEnabledBySetting.") if $context->extendedAttributes->{ConstructorEnabledBySetting} eq "VALUE_IS_MISSING";
+
+        my @settings = split(/&/, $context->extendedAttributes->{ConstructorEnabledBySetting});
+        foreach my $setting (@settings) {
+            push(@conjuncts, "downcast<Document>(jsCast<JSDOMGlobalObject*>(" . $globalObjectPtr . ")->scriptExecutionContext())->settings()." . ToMethodName($setting) . "Enabled()");
+        }
+    }
+
     my $result = join(" && ", @conjuncts);
     $result = "($result)" if @conjuncts > 1;
     return $result;
@@ -7261,6 +7270,14 @@ sub GenerateConstructorDefinition
             push(@$outputArray, "    auto* castedThis = jsCast<${constructorClassName}*>(callFrame->jsCallee());\n");
             push(@$outputArray, "    ASSERT(castedThis);\n");
 
+             if ($interface->extendedAttributes->{ConstructorEnabledBySetting}) {
+                 my $runtimeEnableConditionalString = GenerateRuntimeEnableConditionalString($interface, $operation, "lexicalGlobalObject");
+                 push(@$outputArray, "    if (!${runtimeEnableConditionalString}) {\n");
+                 push(@$outputArray, "        throwTypeError(lexicalGlobalObject, throwScope, \"Illegal constructor\"_s);\n");
+                 push(@$outputArray, "        return JSValue::encode(jsNull());\n");
+                 push(@$outputArray, "    }\n");
+             }
+
             GenerateArgumentsCountCheck($outputArray, $operation, $interface, "    ");
 
             my $functionImplementationName = $generatingNamedConstructor ? "createForJSConstructor" : "create";
@@ -7394,7 +7411,16 @@ sub GenerateConstructorHelperMethods
     }
 
     push(@$outputArray, "    putDirect(vm, vm.propertyNames->name, jsNontrivialString(vm, String(\"$visibleInterfaceName\"_s)), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);\n");
-    push(@$outputArray, "    putDirect(vm, vm.propertyNames->length, jsNumber(${leastConstructorLength}), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);\n") if defined $leastConstructorLength;
+
+    if ($interface->extendedAttributes->{ConstructorEnabledBySetting}) {
+        my $runtimeEnableConditionalString = GenerateRuntimeEnableConditionalString($interface, $interface, "&globalObject");
+        push(@$outputArray, "    int constructorLength = ${leastConstructorLength};\n");
+        push(@$outputArray, "    if (!${runtimeEnableConditionalString})\n");
+        push(@$outputArray, "        constructorLength = 0;\n");
+        push(@$outputArray, "    putDirect(vm, vm.propertyNames->length, jsNumber(constructorLength), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);\n");
+    } else {
+        push(@$outputArray, "    putDirect(vm, vm.propertyNames->length, jsNumber(${leastConstructorLength}), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);\n");
+    }
 
     my $classForThis = "${className}::info()";
     if ($interface->isCallback) {
@@ -7407,7 +7433,7 @@ sub GenerateConstructorHelperMethods
     foreach my $operationOrAttribute (@runtimeEnabledProperties) {
         my $conditionalString = $codeGenerator->GenerateConditionalString($operationOrAttribute);
         push(@$outputArray, "#if ${conditionalString}\n") if $conditionalString;
-        my $runtimeEnableConditionalString = GenerateRuntimeEnableConditionalString($interface, $operationOrAttribute, "true");
+        my $runtimeEnableConditionalString = GenerateRuntimeEnableConditionalString($interface, $operationOrAttribute, "&globalObject");
         my $name = $operationOrAttribute->name;
         push(@$outputArray, "    if (!${runtimeEnableConditionalString}) {\n");
         push(@$outputArray, "        auto propertyName = Identifier::fromString(vm, reinterpret_cast<const LChar*>(\"$name\"), strlen(\"$name\"));\n");
index 9f4adc3..9559e2c 100644 (file)
             "contextsAllowed": ["interface"],
             "values": ["Document", "ExecState", "RuntimeFlags", "ScriptExecutionContext"]
         },
+        "ConstructorEnabledBySetting": {
+            "contextsAllowed": ["attribute", "interface"],
+            "values": ["*"]
+        },
         "ConstructorMayThrowException": {
             "contextsAllowed": ["interface"]
         },
index 42c51fe..a4d8d3e 100644 (file)
@@ -283,7 +283,8 @@ sub GenerateConstructorAttributes
     foreach my $attributeName (sort keys %{$extendedAttributes}) {
       next unless ($attributeName eq "Conditional" || $attributeName eq "EnabledAtRuntime" || $attributeName eq "EnabledForWorld"
         || $attributeName eq "EnabledBySetting" || $attributeName eq "SecureContext" || $attributeName eq "PrivateIdentifier"
-        || $attributeName eq "PublicIdentifier" || $attributeName eq "DisabledByQuirk" || $attributeName eq "EnabledByQuirk" || $attributeName eq "EnabledForContext" || $attributeName eq "CustomEnabled");
+        || $attributeName eq "PublicIdentifier" || $attributeName eq "DisabledByQuirk" || $attributeName eq "EnabledByQuirk"
+        || $attributeName eq "EnabledForContext" || $attributeName eq "CustomEnabled") || $attributeName eq "ConstructorEnabledBySetting";
       my $extendedAttribute = $attributeName;
       $extendedAttribute .= "=" . $extendedAttributes->{$attributeName} unless $extendedAttributes->{$attributeName} eq "VALUE_IS_MISSING";
       push(@extendedAttributesList, $extendedAttribute);
index 402c5ba..3011993 100644 (file)
@@ -21,6 +21,7 @@
     Constructor,
     ConstructorCallWith=Document,
     CustomToJSObject,
+    JSGenerateToNativeObject,
     DOMJIT,
 ] interface DocumentFragment : Node {
 };
index 638265c..99d6e96 100644 (file)
@@ -962,6 +962,11 @@ inline void HTMLMediaElement::updateRenderer()
 {
     if (auto* renderer = this->renderer())
         renderer->updateFromElement();
+
+#if ENABLE(MEDIA_CONTROLS_SCRIPT)
+    if (m_mediaControlsHost)
+        m_mediaControlsHost->updateCaptionDisplaySizes();
+#endif
 }
 
 void HTMLMediaElement::didAttachRenderers()
@@ -1738,10 +1743,7 @@ void HTMLMediaElement::updateActiveTextTrackCues(const MediaTime& movieTime)
 
     for (size_t i = 0; i < currentCuesSize; ++i) {
         RefPtr<TextTrackCue> cue = currentCues[i].data();
-
-        if (cue->isRenderable())
-            toVTTCue(cue.get())->updateDisplayTree(movieTime);
-
+        cue->updateDisplayTree(movieTime);
         if (!cue->isActive())
             activeSetChanged = true;
     }
@@ -2054,7 +2056,8 @@ void HTMLMediaElement::textTrackRemoveCue(TextTrack&, TextTrackCue& cue)
     // Since the cue will be removed from the media element and likely the
     // TextTrack might also be destructed, notifying the region of the cue
     // removal shouldn't be done.
-    if (cue.isRenderable())
+    auto isVTT = is<VTTCue>(cue);
+    if (isVTT)
         toVTTCue(&cue)->notifyRegionWhenRemovingDisplayTree(false);
 
     size_t index = m_currentlyActiveCues.find(interval);
@@ -2063,11 +2066,10 @@ void HTMLMediaElement::textTrackRemoveCue(TextTrack&, TextTrackCue& cue)
         m_currentlyActiveCues.remove(index);
     }
 
-    if (cue.isRenderable())
-        toVTTCue(&cue)->removeDisplayTree();
+    cue.removeDisplayTree();
     updateActiveTextTrackCues(currentMediaTime());
 
-    if (cue.isRenderable())
+    if (isVTT)
         toVTTCue(&cue)->notifyRegionWhenRemovingDisplayTree(true);
 }
 
@@ -6599,6 +6601,7 @@ void HTMLMediaElement::configureTextTrackDisplay(TextTrackVisibilityCheckType ch
         return;
 
     ensureMediaControlsShadowRoot();
+    updateTextTrackDisplay();
 #else
     if (!m_haveVisibleTextTrack && !hasMediaControls())
         return;
@@ -6622,7 +6625,7 @@ void HTMLMediaElement::captionPreferencesChanged()
 
 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
     if (m_mediaControlsHost)
-        m_mediaControlsHost->updateCaptionDisplaySizes();
+        m_mediaControlsHost->updateCaptionDisplaySizes(MediaControlsHost::ForceUpdate::Yes);
 #endif
 
     if (m_player)
index cddc5b7..39a1d3a 100644 (file)
@@ -58,6 +58,7 @@
 #include "RenderView.h"
 #include "Settings.h"
 #include "ShadowRoot.h"
+#include "TextTrackCueGeneric.h"
 #include "TextTrackList.h"
 #include "VTTRegionList.h"
 #include <wtf/IsoMallocInlines.h>
@@ -1125,14 +1126,13 @@ void MediaControlTextTrackContainerElement::updateDisplay()
     // 1. If the media element is an audio element, or is another playback
     // mechanism with no rendering area, abort these steps. There is nothing to
     // render.
-    if (!mediaElement || !mediaElement->isVideo())
+    if (!mediaElement || !mediaElement->isVideo() || m_videoDisplaySize.size().isEmpty())
         return;
 
     // 2. Let video be the media element or other playback mechanism.
     HTMLVideoElement& video = downcast<HTMLVideoElement>(*mediaElement);
 
     // 3. Let output be an empty list of absolutely positioned CSS block boxes.
-    Vector<RefPtr<HTMLDivElement>> output;
 
     // 4. If the user agent is exposing a user interface for video, add to
     // output one or more completely transparent positioned CSS block boxes that
@@ -1173,17 +1173,12 @@ void MediaControlTextTrackContainerElement::updateDisplay()
         removeChildren();
 
     activeCues.removeAllMatching([] (CueInterval& cueInterval) {
-        if (!is<VTTCue>(cueInterval.data()))
-            return true;
-
-        Ref<VTTCue> cue = downcast<VTTCue>(*cueInterval.data());
-
-        return !cue->isRenderable()
-            || !cue->track()
+        RefPtr<TextTrackCue> cue = cueInterval.data();
+        return !cue->track()
             || !cue->track()->isRendered()
             || cue->track()->mode() == TextTrack::Mode::Disabled
             || !cue->isActive()
-            || cue->text().isEmpty();
+            || !cue->isRenderable();
     });
 
     // Sort the active cues for the appropriate display order. For example, for roll-up
@@ -1191,37 +1186,20 @@ void MediaControlTextTrackContainerElement::updateDisplay()
     // so that the newest captions appear at the bottom.
     std::sort(activeCues.begin(), activeCues.end(), &compareCueIntervalForDisplay);
 
-    // 10. For each text track cue cue in cues that has not yet had
-    // corresponding CSS boxes added to output, in text track cue order, run the
-    // following substeps:
-    for (size_t i = 0; i < activeCues.size(); ++i) {
-        if (!mediaController()->closedCaptionsVisible())
-            continue;
-
-        RefPtr<VTTCue> cue = downcast<VTTCue>(activeCues[i].data());
-
-        DEBUG_LOG(LOGIDENTIFIER, "adding and positioning cue ", i, ": \"", cue->text(), "\", start=", cue->startTime(), ", end=", cue->endTime(), ", line=", cue->line());
-        Ref<VTTCueBox> displayBox = cue->getDisplayTree(m_videoDisplaySize.size(), m_fontSize);
-        RefPtr<VTTRegion> region = cue->track()->regions()->getRegionById(cue->regionId());
-        if (!region) {
-            // If cue has an empty text track cue region identifier or there is no
-            // WebVTT region whose region identifier is identical to cue's text
-            // track cue region identifier, run the following substeps:
-            if (displayBox->hasChildNodes() && !contains(displayBox.ptr())) {
-                // Note: the display tree of a cue is removed when the active flag of the cue is unset.
-                appendChild(displayBox);
-                cue->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
+    if (mediaController()->closedCaptionsVisible()) {
+        // 10. For each text track cue cue in cues that has not yet had
+        // corresponding CSS boxes added to output, in text track cue order, run the
+        // following substeps:
+        for (auto& interval : activeCues) {
+            auto cue = interval.data();
+            cue->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
+            if (is<VTTCue>(cue) || is<TextTrackCueGeneric>(cue))
+                processActiveVTTCue(*toVTTCue(cue));
+            else {
+                auto displayBox = cue->getDisplayTree(m_videoDisplaySize.size(), m_fontSize);
+                if (displayBox->hasChildNodes() && !contains(displayBox.get()))
+                    appendChild(*displayBox);
             }
-        } else {
-            // Let region be the WebVTT region whose region identifier
-            // matches the text track cue region identifier of cue.
-            Ref<HTMLDivElement> regionNode = region->getDisplayTree();
-
-            // Append the region to the viewport, if it was not already.
-            if (!contains(regionNode.ptr()))
-                appendChild(region->getDisplayTree());
-
-            region->appendTextTrackCueBox(WTFMove(displayBox));
         }
     }
 
@@ -1235,6 +1213,33 @@ void MediaControlTextTrackContainerElement::updateDisplay()
     }
 }
 
+void MediaControlTextTrackContainerElement::processActiveVTTCue(VTTCue& cue)
+{
+    ASSERT(is<VTTCue>(cue) || is<TextTrackCueGeneric>(cue));
+
+    DEBUG_LOG(LOGIDENTIFIER, "adding and positioning cue: \"", cue.text(), "\", start=", cue.startTime(), ", end=", cue.endTime(), ", line=", cue.line());
+    Ref<TextTrackCueBox> displayBox = *cue.getDisplayTree(m_videoDisplaySize.size(), m_fontSize);
+
+    if (auto region = cue.track()->regions()->getRegionById(cue.regionId())) {
+        // Let region be the WebVTT region whose region identifier
+        // matches the text track cue region identifier of cue.
+        Ref<HTMLDivElement> regionNode = region->getDisplayTree();
+
+        if (!contains(regionNode.ptr()))
+            appendChild(region->getDisplayTree());
+
+        region->appendTextTrackCueBox(WTFMove(displayBox));
+    } else {
+        // If cue has an empty text track cue region identifier or there is no
+        // WebVTT region whose region identifier is identical to cue's text
+        // track cue region identifier, run the following substeps:
+        if (displayBox->hasChildNodes() && !contains(displayBox.ptr())) {
+            // Note: the display tree of a cue is removed when the active flag of the cue is unset.
+            appendChild(displayBox);
+        }
+    }
+}
+
 void MediaControlTextTrackContainerElement::updateActiveCuesFontSize()
 {
     if (!document().page())
@@ -1250,12 +1255,9 @@ void MediaControlTextTrackContainerElement::updateActiveCuesFontSize()
 
     for (auto& activeCue : mediaElement->currentlyActiveCues()) {
         RefPtr<TextTrackCue> cue = activeCue.data();
-        if (!cue->isRenderable())
-            continue;
-
-        toVTTCue(cue.get())->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
+        if (cue->isRenderable())
+            cue->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
     }
-
 }
 
 void MediaControlTextTrackContainerElement::updateTextStrokeStyle()
@@ -1295,6 +1297,13 @@ void MediaControlTextTrackContainerElement::updateTimerFired()
     if (!document().page())
         return;
 
+    auto mediaElement = parentMediaElement(this);
+    if (!mediaElement)
+        return;
+
+    for (auto& activeCue : mediaElement->currentlyActiveCues())
+        activeCue.data()->recalculateStyles();
+
     if (m_textTrackRepresentation)
         updateStyleForTextTrackRepresentation();
 
@@ -1312,7 +1321,7 @@ void MediaControlTextTrackContainerElement::updateTextTrackRepresentation()
     if (!mediaElement->requiresTextTrackRepresentation()) {
         if (m_textTrackRepresentation) {
             clearTextTrackRepresentation();
-            updateSizes(true);
+            updateSizes(ForceUpdate::Yes);
         }
         return;
     }
@@ -1346,6 +1355,7 @@ void MediaControlTextTrackContainerElement::updateStyleForTextTrackRepresentatio
 {
     if (!m_updateTextTrackRepresentationStyle)
         return;
+
     m_updateTextTrackRepresentationStyle = false;
 
     if (m_textTrackRepresentation) {
@@ -1368,16 +1378,16 @@ void MediaControlTextTrackContainerElement::enteredFullscreen()
 {
     if (hasChildNodes())
         updateTextTrackRepresentation();
-    updateSizes(true);
+    updateSizes(ForceUpdate::Yes);
 }
 
 void MediaControlTextTrackContainerElement::exitedFullscreen()
 {
     clearTextTrackRepresentation();
-    updateSizes(true);
+    updateSizes(ForceUpdate::Yes);
 }
 
-void MediaControlTextTrackContainerElement::updateSizes(bool forceUpdate)
+void MediaControlTextTrackContainerElement::updateSizes(ForceUpdate force)
 {
     auto mediaElement = parentMediaElement(this);
     if (!mediaElement)
@@ -1398,7 +1408,7 @@ void MediaControlTextTrackContainerElement::updateSizes(bool forceUpdate)
         videoBox = downcast<RenderVideo>(*mediaElement->renderer()).videoBox();
     }
 
-    if (!forceUpdate && m_videoDisplaySize == videoBox)
+    if (force == ForceUpdate::No && m_videoDisplaySize == videoBox)
         return;
 
     m_videoDisplaySize = videoBox;
index 7c2cc65..eab558f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2019 Apple Inc. All rights reserved.
  * Copyright (C) 2012 Google Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -467,6 +467,8 @@ private:
 
 #if ENABLE(VIDEO_TRACK)
 
+class VTTCue;
+
 class MediaControlTextTrackContainerElement final
     : public MediaControlDivElement
     , public TextTrackRepresentationClient
@@ -478,8 +480,10 @@ class MediaControlTextTrackContainerElement final
 public:
     static Ref<MediaControlTextTrackContainerElement> create(Document&);
 
+    enum class ForceUpdate { Yes, No };
+    void updateSizes(ForceUpdate force = ForceUpdate::No);
+
     void updateDisplay();
-    void updateSizes(bool forceUpdate = false);
     void enteredFullscreen();
     void exitedFullscreen();
 
@@ -487,6 +491,7 @@ private:
     void updateTimerFired();
     void updateActiveCuesFontSize();
     void updateTextStrokeStyle();
+    void processActiveVTTCue(VTTCue&);
 
 #if !RELEASE_LOG_DISABLED
     const Logger& logger() const final;
index a7dbdad..64c88e5 100644 (file)
@@ -410,7 +410,7 @@ void MediaControls::textTrackPreferencesChanged()
 {
     closedCaptionTracksChanged();
     if (m_textDisplayContainer)
-        m_textDisplayContainer->updateSizes(true);
+        m_textDisplayContainer->updateSizes(MediaControlTextTrackContainerElement::ForceUpdate::Yes);
 }
 
 void MediaControls::clearTextDisplayContainer()
index 59a1924..180c96c 100644 (file)
@@ -237,11 +237,8 @@ void TextTrack::setMode(Mode mode)
         m_client->textTrackRemoveCues(*this, *m_cues);
 
     if (mode != Mode::Showing && m_cues) {
-        for (size_t i = 0; i < m_cues->length(); ++i) {
-            RefPtr<TextTrackCue> cue = m_cues->item(i);
-            if (cue->isRenderable())
-                toVTTCue(cue.get())->removeDisplayTree();
-        }
+        for (size_t i = 0; i < m_cues->length(); ++i)
+            m_cues->item(i)->removeDisplayTree();
     }
 
     m_mode = mode;
index 8eac751..fac06bd 100644 (file)
 
 #include "CSSPropertyNames.h"
 #include "CSSValueKeywords.h"
+#include "DOMRect.h"
 #include "Event.h"
+#include "HTMLCollection.h"
+#include "HTMLDivElement.h"
+#include "HTMLStyleElement.h"
 #include "Logging.h"
 #include "NodeTraversal.h"
+#include "Page.h"
+#include "ScriptDisallowedScope.h"
 #include "Text.h"
 #include "TextTrack.h"
 #include "TextTrackCueList.h"
 #include <wtf/IsoMallocInlines.h>
 #include <wtf/MathExtras.h>
 #include <wtf/NeverDestroyed.h>
+#include <wtf/OptionSet.h>
 #include <wtf/text/StringConcatenateNumbers.h>
 
 namespace WebCore {
 
 WTF_MAKE_ISO_ALLOCATED_IMPL(TextTrackCue);
+WTF_MAKE_ISO_ALLOCATED_IMPL(TextTrackCueBox);
 
 const AtomString& TextTrackCue::cueShadowPseudoId()
 {
@@ -60,12 +68,165 @@ const AtomString& TextTrackCue::cueShadowPseudoId()
     return cue;
 }
 
+const AtomString& TextTrackCue::cueBoxShadowPseudoId()
+{
+    static NeverDestroyed<const AtomString> trackDisplayBoxShadowPseudoId("-webkit-media-text-track-display", AtomString::ConstructFromLiteral);
+    return trackDisplayBoxShadowPseudoId;
+}
+
+const AtomString& TextTrackCue::cueBackdropShadowPseudoId()
+{
+    static NeverDestroyed<const AtomString> cueBackdropShadowPseudoId("-webkit-media-text-track-display-backdrop", AtomString::ConstructFromLiteral);
+    return cueBackdropShadowPseudoId;
+}
+
+static const QualifiedName& cueAttributName()
+{
+    static NeverDestroyed<QualifiedName> cueTag(nullAtom(), "cue", nullAtom());
+    return cueTag;
+}
+
+static const QualifiedName& cueBackgroundAttributName()
+{
+    static NeverDestroyed<QualifiedName> cueBackgroundTag(nullAtom(), "cuebackground", nullAtom());
+    return cueBackgroundTag;
+}
+
+TextTrackCueBox::TextTrackCueBox(Document& document, TextTrackCue& cue)
+    : HTMLElement(HTMLNames::divTag, document)
+    , m_cue(makeWeakPtr(cue))
+{
+    setHasCustomStyleResolveCallbacks();
+    setPseudo(TextTrackCue::cueBoxShadowPseudoId());
+}
+
+TextTrackCue* TextTrackCueBox::getCue() const
+{
+    return m_cue.get();
+}
+
+static inline bool isLegalNode(Node& node)
+{
+    return node.hasTagName(HTMLNames::brTag)
+        || node.hasTagName(HTMLNames::divTag)
+        || node.hasTagName(HTMLNames::imgTag)
+        || node.hasTagName(HTMLNames::pTag)
+        || node.hasTagName(HTMLNames::rbTag)
+        || node.hasTagName(HTMLNames::rtTag)
+        || node.hasTagName(HTMLNames::rtcTag)
+        || node.hasTagName(HTMLNames::rubyTag)
+        || node.hasTagName(HTMLNames::spanTag)
+        || node.nodeType() == Node::TEXT_NODE;
+}
+
+static Exception invalidNodeException(Node& node)
+{
+    return Exception { InvalidNodeTypeError, makeString("Invalid node type: ", node.nodeName()) };
+}
+
+static ExceptionOr<void> checkForInvalidNodeTypes(Node& root)
+{
+    if (!isLegalNode(root))
+        return invalidNodeException(root);
+
+    for (auto* child = root.firstChild(); child; child = child->nextSibling()) {
+        if (!isLegalNode(*child))
+            return invalidNodeException(*child);
+
+        if (is<ContainerNode>(*child)) {
+            auto result = checkForInvalidNodeTypes(*child);
+            if (result.hasException())
+                return result.releaseException();
+        }
+    }
+
+    return { };
+}
+
+enum RequiredNodes {
+    Cue = 1 << 0,
+    CueBackground = 1 << 1,
+};
+
+static OptionSet<RequiredNodes> tagPseudoObjects(Node& node)
+{
+    if (!is<Element>(node))
+        return { };
+
+    OptionSet<RequiredNodes> nodeTypes = { };
+
+    auto& element = downcast<Element>(node);
+    if (element.hasAttributeWithoutSynchronization(cueAttributName())) {
+        element.setPseudo(TextTrackCue::cueShadowPseudoId());
+        nodeTypes = { RequiredNodes::Cue };
+    } else if (element.hasAttributeWithoutSynchronization(cueBackgroundAttributName())) {
+        element.setPseudo(TextTrackCue::cueBackdropShadowPseudoId());
+        nodeTypes = { RequiredNodes::CueBackground };
+    }
+
+    for (auto* child = element.firstChild(); child; child = child->nextSibling())
+        nodeTypes.add(tagPseudoObjects(*child));
+
+    return nodeTypes;
+}
+
+static void removePseudoAttributes(Node& node)
+{
+    if (!is<Element>(node))
+        return;
+
+    auto& element = downcast<Element>(node);
+    if (element.hasAttributeWithoutSynchronization(cueAttributName()) || element.hasAttributeWithoutSynchronization(cueBackgroundAttributName()))
+        element.removeAttribute(HTMLNames::pseudoAttr);
+
+    for (auto* child = element.firstChild(); child; child = child->nextSibling())
+        removePseudoAttributes(*child);
+}
+
+ExceptionOr<Ref<TextTrackCue>> TextTrackCue::create(ScriptExecutionContext& context, double start, double end, DocumentFragment& cueDocument)
+{
+    ASSERT(context.isDocument());
+    ASSERT(is<DocumentFragment>(cueDocument));
+
+    if (!cueDocument.firstChild())
+        return Exception { InvalidNodeTypeError, "Empty cue fragment" };
+
+    for (Node* node = cueDocument.firstChild(); node; node = node->nextSibling()) {
+        auto result = checkForInvalidNodeTypes(*node);
+        if (result.hasException())
+            return result.releaseException();
+    }
+
+    auto fragment = DocumentFragment::create(downcast<Document>(context));
+    for (Node* node = cueDocument.firstChild(); node; node = node->nextSibling()) {
+        auto result = fragment->ensurePreInsertionValidity(*node, nullptr);
+        if (result.hasException())
+            return result.releaseException();
+    }
+    cueDocument.cloneChildNodes(fragment);
+
+    OptionSet<RequiredNodes> nodeTypes = { };
+    for (Node* node = fragment->firstChild(); node; node = node->nextSibling())
+        nodeTypes.add(tagPseudoObjects(*node));
+
+    if (!nodeTypes.contains(RequiredNodes::Cue))
+        return Exception { InvalidStateError, makeString("Missing required attribute: ", cueAttributName().toString()) };
+    if (!nodeTypes.contains(RequiredNodes::CueBackground))
+        return Exception { InvalidStateError, makeString("Missing required attribute: ", cueBackgroundAttributName().toString()) };
+
+    return adoptRef(*new TextTrackCue(context, MediaTime::createWithDouble(start), MediaTime::createWithDouble(end), WTFMove(fragment.get())));
+}
+
+TextTrackCue::TextTrackCue(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, DocumentFragment&& cueFragment)
+    : TextTrackCue(context, start, end)
+{
+    m_cueNode = &cueFragment;
+}
+
 TextTrackCue::TextTrackCue(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end)
     : m_startTime(start)
     , m_endTime(end)
     , m_scriptExecutionContext(context)
-    , m_isActive(false)
-    , m_pauseOnExit(false)
 {
     ASSERT(m_scriptExecutionContext.isDocument());
 }
@@ -85,6 +246,8 @@ void TextTrackCue::didChange()
     if (--m_processingCueChanges)
         return;
 
+    m_displayTreeNeedsUpdate = true;
+
     if (m_track)
         m_track->cueDidChange(this);
 }
@@ -166,6 +329,13 @@ bool TextTrackCue::isActive()
 void TextTrackCue::setIsActive(bool active)
 {
     m_isActive = active;
+
+    if (m_isActive || !m_displayTree)
+        return;
+
+    // The display tree is never exposed to author scripts so it's safe to dispatch events here.
+    ScriptDisallowedScope::EventAllowedScope allowedScope(*m_displayTree);
+    m_displayTree->remove();
 }
 
 bool TextTrackCue::isOrderedBefore(const TextTrackCue* other) const
@@ -225,8 +395,8 @@ void TextTrackCue::toJSON(JSON::Object& value) const
 {
     ASCIILiteral type = "Generic"_s;
     switch (cueType()) {
-    case TextTrackCue::Generic:
-        type = "Generic"_s;
+    case TextTrackCue::ConvertedToWebVTT:
+        type = "ConvertedToWebVTT"_s;
         break;
     case TextTrackCue::WebVTT:
         type = "WebVTT"_s;
@@ -234,6 +404,9 @@ void TextTrackCue::toJSON(JSON::Object& value) const
     case TextTrackCue::Data:
         type = "Data"_s;
         break;
+    case TextTrackCue::Generic:
+        type = "Generic"_s;
+        break;
     }
 
     value.setString("type"_s, type);
@@ -253,11 +426,108 @@ String TextTrackCue::toJSONString() const
 String TextTrackCue::debugString() const
 {
     String text;
-    if (isRenderable())
+    if (is<VTTCue>(this))
         text = toVTTCue(this)->text();
     return makeString("0x", hex(reinterpret_cast<uintptr_t>(this)), " id=", id(), " interval=", startTime(), "-->", endTime(), " cue=", text, ')');
 }
 
+RefPtr<DocumentFragment> TextTrackCue::getCueAsHTML()
+{
+    if (!m_cueNode)
+        return nullptr;
+
+    auto clonedFragment = DocumentFragment::create(ownerDocument());
+    m_cueNode->cloneChildNodes(clonedFragment);
+
+    for (Node* node = clonedFragment->firstChild(); node; node = node->nextSibling())
+        removePseudoAttributes(*node);
+
+    return clonedFragment;
+}
+
+bool TextTrackCue::isRenderable() const
+{
+    return m_cueNode && m_cueNode->firstChild();
+}
+
+RefPtr<TextTrackCueBox> TextTrackCue::getDisplayTree(const IntSize&, int)
+{
+    if (m_displayTree && !m_displayTreeNeedsUpdate)
+        return m_displayTree;
+
+    rebuildDisplayTree();
+
+    return m_displayTree;
+}
+
+void TextTrackCue::removeDisplayTree()
+{
+    if (!m_displayTree)
+        return;
+
+    // The display tree is never exposed to author scripts so it's safe to dispatch events here.
+    ScriptDisallowedScope::EventAllowedScope allowedScope(*m_displayTree);
+    m_displayTree->remove();
+}
+
+void TextTrackCue::setFontSize(int fontSize, const IntSize&, bool important)
+{
+    if (fontSize == m_fontSize && important == m_fontSizeIsImportant)
+        return;
+
+    m_displayTreeNeedsUpdate = true;
+    m_fontSizeIsImportant = important;
+    m_fontSize = fontSize;
+}
+
+void TextTrackCue::rebuildDisplayTree()
+{
+    if (!m_cueNode)
+        return;
+
+    ScriptDisallowedScope::EventAllowedScope allowedScopeForReferenceTree(*m_cueNode);
+
+    if (!m_displayTree) {
+        m_displayTree = TextTrackCueBox::create(ownerDocument(), *this);
+        m_displayTree->setPseudo(AtomString("-webkit-generic-cue-root", AtomString::ConstructFromLiteral));
+    }
+
+    m_displayTree->removeChildren();
+    auto clonedFragment = DocumentFragment::create(ownerDocument());
+    m_cueNode->cloneChildNodes(clonedFragment);
+    m_displayTree->appendChild(clonedFragment);
+
+    if (m_fontSize && ownerDocument().page()) {
+        StringBuilder builder;
+        builder.append(ownerDocument().page()->captionUserPreferencesStyleSheet());
+        builder.appendLiteral(" ::");
+        builder.append(TextTrackCue::cueShadowPseudoId());
+        builder.append('{');
+        builder.append(getPropertyNameString(CSSPropertyFontSize));
+        builder.append(':');
+        builder.append(makeString(m_fontSize, "px"));
+        if (m_fontSizeIsImportant)
+            builder.appendLiteral(" !important");
+        builder.appendLiteral("; }");
+
+        auto style = HTMLStyleElement::create(HTMLNames::styleTag, ownerDocument(), false);
+        style->setTextContent(builder.toString());
+        m_displayTree->appendChild(style);
+    }
+
+    if (track()) {
+        if (const auto& styleSheets = track()->styleSheets()) {
+            for (const auto& cssString : *styleSheets) {
+                auto style = HTMLStyleElement::create(HTMLNames::styleTag, m_displayTree->document(), false);
+                style->setTextContent(cssString);
+                m_displayTree->appendChild(style);
+            }
+        }
+    }
+
+    m_displayTreeNeedsUpdate = false;
+}
+
 } // namespace WebCore
 
 #endif
index aeddbd3..ebb4597 100644 (file)
 #if ENABLE(VIDEO_TRACK)
 
 #include "Document.h"
+#include "DocumentFragment.h"
+#include "HTMLElement.h"
 #include <wtf/JSONValues.h>
 #include <wtf/MediaTime.h>
 
 namespace WebCore {
 
 class TextTrack;
+class TextTrackCue;
 
-class TextTrackCue : public RefCounted<TextTrackCue>, public EventTargetWithInlineData {
+class TextTrackCueBox : public HTMLElement {
+    WTF_MAKE_ISO_ALLOCATED(TextTrackCueBox);
+public:
+    static Ref<TextTrackCueBox> create(Document& document, TextTrackCue& cue)
+    {
+        return adoptRef(*new TextTrackCueBox(document, cue));
+    }
+
+    TextTrackCue* getCue() const;
+    virtual void applyCSSProperties(const IntSize&) { }
+
+protected:
+    TextTrackCueBox(Document&, TextTrackCue&);
+    ~TextTrackCueBox() { }
+
+private:
+
+    WeakPtr<TextTrackCue> m_cue;
+};
+
+class TextTrackCue : public RefCounted<TextTrackCue>, public EventTargetWithInlineData, public CanMakeWeakPtr<TextTrackCue> {
     WTF_MAKE_ISO_ALLOCATED(TextTrackCue);
 public:
     static const AtomString& cueShadowPseudoId();
+    static const AtomString& cueBackdropShadowPseudoId();
+    static const AtomString& cueBoxShadowPseudoId();
+
+    static ExceptionOr<Ref<TextTrackCue>> create(ScriptExecutionContext&, double start, double end, DocumentFragment&);
 
     TextTrack* track() const;
     void setTrack(TextTrack*);
@@ -75,9 +102,9 @@ public:
 
     bool hasEquivalentStartTime(const TextTrackCue&) const;
 
-    enum CueType { Data, Generic, WebVTT };
-    virtual CueType cueType() const = 0;
-    virtual bool isRenderable() const { return false; }
+    enum CueType { Generic, Data, ConvertedToWebVTT, WebVTT };
+    virtual CueType cueType() const { return CueType::Generic; }
+    virtual bool isRenderable() const;
 
     enum CueMatchRules { MatchAllFields, IgnoreDuration };
     virtual bool isEqual(const TextTrackCue&, CueMatchRules) const;
@@ -86,13 +113,23 @@ public:
     void willChange();
     virtual void didChange();
 
+    virtual RefPtr<TextTrackCueBox> getDisplayTree(const IntSize&, int);
+    virtual void removeDisplayTree();
+
+    virtual RefPtr<DocumentFragment> getCueAsHTML();
+
     String toJSONString() const;
     String debugString() const;
 
     using RefCounted::ref;
     using RefCounted::deref;
 
+    virtual void recalculateStyles() { m_displayTreeNeedsUpdate = true; }
+    virtual void setFontSize(int, const IntSize&, bool important);
+    virtual void updateDisplayTree(const MediaTime&) { };
+
 protected:
+    TextTrackCue(ScriptExecutionContext&, const MediaTime& start, const MediaTime& end, DocumentFragment&&);
     TextTrackCue(ScriptExecutionContext&, const MediaTime& start, const MediaTime& end);
 
     Document& ownerDocument() { return downcast<Document>(m_scriptExecutionContext); }
@@ -111,6 +148,8 @@ private:
 
     virtual bool cueContentsMatch(const TextTrackCue&) const;
 
+    void rebuildDisplayTree();
+
     String m_id;
     MediaTime m_startTime;
     MediaTime m_endTime;
@@ -120,8 +159,15 @@ private:
 
     ScriptExecutionContext& m_scriptExecutionContext;
 
-    bool m_isActive : 1;
-    bool m_pauseOnExit : 1;
+    RefPtr<DocumentFragment> m_cueNode;
+    RefPtr<TextTrackCueBox> m_displayTree;
+
+    int m_fontSize { 0 };
+    bool m_fontSizeIsImportant { false };
+
+    bool m_isActive { false };
+    bool m_pauseOnExit { false };
+    bool m_displayTreeNeedsUpdate { true };
 };
 
 } // namespace WebCore
index 44fe168..d54d015 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
     CustomToJSObject,
     JSCustomMarkFunction,
     SkipVTableValidation,
+    Constructor(double startTime, double endTime, DocumentFragment cueNode),
+    ConstructorCallWith=ScriptExecutionContext,
+    ConstructorEnabledBySetting=GenericCueAPI,
+    ConstructorMayThrowException,
 ] interface TextTrackCue : EventTarget {
     readonly attribute TextTrack track;
 
@@ -39,4 +44,6 @@
 
     attribute EventHandler onenter;
     attribute EventHandler onexit;
+
+    [EnabledBySetting=GenericCueAPI] DocumentFragment getCueAsHTML();
 };
index 15dfa23..c4b6fd9 100644 (file)
@@ -229,7 +229,7 @@ bool TextTrackCueGeneric::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatc
     if (!TextTrackCue::isEqual(cue, match))
         return false;
 
-    if (cue.cueType() != TextTrackCue::Generic)
+    if (cue.cueType() != TextTrackCue::ConvertedToWebVTT)
         return false;
 
     return cueContentsMatch(cue);
@@ -249,7 +249,7 @@ bool TextTrackCueGeneric::isOrderedBefore(const TextTrackCue* that) const
     if (VTTCue::isOrderedBefore(that))
         return true;
 
-    if (that->cueType() == Generic && startTime() == that->startTime() && endTime() == that->endTime()) {
+    if (that->cueType() == ConvertedToWebVTT && startTime() == that->startTime() && endTime() == that->endTime()) {
         // Further order generic cues by their calculated line value.
         std::pair<double, double> thisPosition = getPositionCoordinates();
         std::pair<double, double> thatPosition = toVTTCue(that)->getPositionCoordinates();
@@ -261,14 +261,14 @@ bool TextTrackCueGeneric::isOrderedBefore(const TextTrackCue* that) const
 
 bool TextTrackCueGeneric::isPositionedAbove(const TextTrackCue* that) const
 {
-    if (that->cueType() == Generic && startTime() == that->startTime() && endTime() == that->endTime()) {
+    if (that->cueType() == ConvertedToWebVTT && startTime() == that->startTime() && endTime() == that->endTime()) {
         // Further order generic cues by their calculated line value.
         std::pair<double, double> thisPosition = getPositionCoordinates();
         std::pair<double, double> thatPosition = toVTTCue(that)->getPositionCoordinates();
         return thisPosition.second > thatPosition.second || (thisPosition.second == thatPosition.second && thisPosition.first < thatPosition.first);
     }
     
-    if (that->cueType() == Generic)
+    if (that->cueType() == ConvertedToWebVTT)
         return startTime() > that->startTime();
     
     return VTTCue::isOrderedBefore(that);
index 2cd7100..fd0cb3e 100644 (file)
@@ -82,7 +82,7 @@ private:
     bool cueContentsMatch(const TextTrackCue&) const final;
     bool doesExtendCue(const TextTrackCue&) const final;
 
-    CueType cueType() const final { return Generic; }
+    CueType cueType() const final { return ConvertedToWebVTT; }
 
     Color m_foregroundColor;
     Color m_backgroundColor;
@@ -111,7 +111,7 @@ struct LogArgument<WebCore::TextTrackCueGeneric> {
 }
 
 SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::TextTrackCueGeneric)
-static bool isType(const WebCore::TextTrackCue& cue) { return cue.cueType() == WebCore::TextTrackCue::Generic; }
+static bool isType(const WebCore::TextTrackCue& cue) { return cue.cueType() == WebCore::TextTrackCue::ConvertedToWebVTT; }
 SPECIALIZE_TYPE_TRAITS_END()
 
 #endif
index 9e02c26..8dfebd2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2011, 2013 Google Inc.  All rights reserved.
- * Copyright (C) 2011-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -47,6 +47,7 @@
 #include "ScriptDisallowedScope.h"
 #include "Text.h"
 #include "TextTrack.h"
+#include "TextTrackCueGeneric.h"
 #include "TextTrackCueList.h"
 #include "VTTRegionList.h"
 #include "VTTScanner.h"
@@ -59,8 +60,8 @@
 
 namespace WebCore {
 
-WTF_MAKE_ISO_ALLOCATED_IMPL(VTTCueBox);
 WTF_MAKE_ISO_ALLOCATED_IMPL(VTTCue);
+WTF_MAKE_ISO_ALLOCATED_IMPL(VTTCueBox);
 
 // This constant should correspond with the percentage returned by CaptionUserPreferences::captionFontSizeScaleAndImportance.
 static constexpr double DEFAULTCAPTIONFONTSIZEPERCENTAGE = 5;
@@ -126,31 +127,19 @@ static const String& verticalGrowingRightKeyword()
 
 // ----------------------------
 
-Ref<VTTCueBox> VTTCueBox::create(Document& document, VTTCue& cue)
-{
-    VTTCueBox& cueBox = *new VTTCueBox(document, cue);
-    cueBox.setPseudo(VTTCueBox::vttCueBoxShadowPseudoId());
-    return adoptRef(cueBox);
-}
-
 VTTCueBox::VTTCueBox(Document& document, VTTCue& cue)
-    : HTMLElement(divTag, document)
-    , m_cue(makeWeakPtr(cue))
-{
-    setPseudo(vttCueBoxShadowPseudoId());
-}
-
-VTTCue* VTTCueBox::getCue() const
+    : TextTrackCueBox(document, cue)
 {
-    return m_cue.get();
 }
 
 void VTTCueBox::applyCSSProperties(const IntSize& videoSize)
 {
-    if (!m_cue)
+    auto textTrackCue = getCue();
+    if (!textTrackCue)
         return;
 
-    auto cue = makeRef(*m_cue);
+    ASSERT(is<VTTCue>(textTrackCue) || is<TextTrackCueGeneric>(textTrackCue));
+    auto cue = makeRef(*toVTTCue(textTrackCue));
 
     // FIXME: Apply all the initial CSS positioning properties. http://wkb.ug/79916
     if (!cue->regionId().isEmpty()) {
@@ -242,12 +231,6 @@ void VTTCueBox::applyCSSProperties(const IntSize& videoSize)
     cue->element().setInlineStyleProperty(CSSPropertyOverflow, CSSValueVisible);
 }
 
-const AtomString& VTTCueBox::vttCueBoxShadowPseudoId()
-{
-    static NeverDestroyed<const AtomString> trackDisplayBoxShadowPseudoId("-webkit-media-text-track-display", AtomString::ConstructFromLiteral);
-    return trackDisplayBoxShadowPseudoId;
-}
-
 RenderPtr<RenderElement> VTTCueBox::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
 {
     return createRenderer<RenderVTTCue>(*this, WTFMove(style));
@@ -255,12 +238,6 @@ RenderPtr<RenderElement> VTTCueBox::createElementRenderer(RenderStyle&& style, c
 
 // ----------------------------
 
-const AtomString& VTTCue::cueBackdropShadowPseudoId()
-{
-    static NeverDestroyed<const AtomString> cueBackdropShadowPseudoId("-webkit-media-text-track-display-backdrop", AtomString::ConstructFromLiteral);
-    return cueBackdropShadowPseudoId;
-}
-
 Ref<VTTCue> VTTCue::create(ScriptExecutionContext& context, const WebVTTCueData& data)
 {
     return adoptRef(*new VTTCue(context, data));
@@ -867,11 +844,11 @@ void VTTCue::updateDisplayTree(const MediaTime& movieTime)
     m_cueHighlightBox->appendChild(*referenceTree);
 }
 
-VTTCueBox& VTTCue::getDisplayTree(const IntSize& videoSize, int fontSize)
+RefPtr<TextTrackCueBox> VTTCue::getDisplayTree(const IntSize& videoSize, int fontSize)
 {
     Ref<VTTCueBox> displayTree = displayTreeInternal();
     if (!m_displayTreeShouldChange || !track()->isRendered())
-        return displayTree.get();
+        return displayTree;
 
     // 10.1 - 10.10
     calculateDisplayParameters();
@@ -917,15 +894,28 @@ VTTCueBox& VTTCue::getDisplayTree(const IntSize& videoSize, int fontSize)
         }
     }
 
+    if (m_fontSize)
+        displayTree->setInlineStyleProperty(CSSPropertyFontSize, m_fontSize, CSSPrimitiveValue::CSS_PX, m_fontSizeIsImportant);
+
     m_displayTreeShouldChange = false;
 
+    if (track()) {
+        if (auto* regions = track()->regions()) {
+            if (auto region = regions->getRegionById(m_regionId))
+                region->cueStyleChanged();
+        }
+    }
+
     // 10.15. Let cue's text track cue display state have the CSS boxes in
     // boxes.
-    return displayTree.get();
+    return displayTree;
 }
 
 void VTTCue::removeDisplayTree()
 {
+    if (!hasDisplayTree())
+        return;
+
     // The region needs to be informed about the cue removal.
     if (m_notifyRegion && track()) {
         if (VTTRegionList* regions = track()->regions()) {
@@ -936,9 +926,6 @@ void VTTCue::removeDisplayTree()
         }
     }
 
-    if (!hasDisplayTree())
-        return;
-
     // The display tree is never exposed to author scripts so it's safe to dispatch events here.
     ScriptDisallowedScope::EventAllowedScope allowedScope(displayTreeInternal());
     displayTreeInternal().remove();
@@ -1248,11 +1235,12 @@ bool VTTCue::doesExtendCue(const TextTrackCue& cue) const
     
 void VTTCue::setFontSize(int fontSize, const IntSize&, bool important)
 {
-    if (!hasDisplayTree() || !fontSize)
+    if (fontSize == m_fontSize && important == m_fontSizeIsImportant)
         return;
 
     m_displayTreeShouldChange = true;
-    displayTreeInternal().setInlineStyleProperty(CSSPropertyFontSize, fontSize, CSSPrimitiveValue::CSS_PX, important);
+    m_fontSizeIsImportant = important;
+    m_fontSize = fontSize;
 }
 
 VTTCue* toVTTCue(TextTrackCue* cue)
@@ -1262,7 +1250,7 @@ VTTCue* toVTTCue(TextTrackCue* cue)
 
 const VTTCue* toVTTCue(const TextTrackCue* cue)
 {
-    ASSERT_WITH_SECURITY_IMPLICATION(cue->isRenderable());
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(is<VTTCue>(cue) || is<TextTrackCueGeneric>(cue));
     return static_cast<const VTTCue*>(cue);
 }
 
index 1bf7f55..4515142 100644 (file)
@@ -49,15 +49,16 @@ class WebVTTCueData;
 
 // ----------------------------
 
-class VTTCueBox : public HTMLElement {
+class VTTCueBox : public TextTrackCueBox {
     WTF_MAKE_ISO_ALLOCATED(VTTCueBox);
 public:
-    static Ref<VTTCueBox> create(Document&, VTTCue&);
+    static Ref<VTTCueBox> create(Document& document, VTTCue& cue)
+    {
+        return adoptRef(*new VTTCueBox(document, cue));
+    }
 
-    VTTCue* getCue() const;
-    virtual void applyCSSProperties(const IntSize& videoSize);
+    void applyCSSProperties(const IntSize&) override;
 
-    static const AtomString& vttCueBoxShadowPseudoId();
     void setFontSizeFromCaptionUserPrefs(int fontSize) { m_fontSizeFromCaptionUserPrefs = fontSize; }
 
 protected:
@@ -74,7 +75,7 @@ private:
 
 // ----------------------------
 
-class VTTCue : public TextTrackCue, public CanMakeWeakPtr<VTTCue> {
+class VTTCue : public TextTrackCue {
     WTF_MAKE_ISO_ALLOCATED(VTTCue);
 public:
     static Ref<VTTCue> create(ScriptExecutionContext& context, double start, double end, const String& content)
@@ -89,8 +90,6 @@ public:
 
     static Ref<VTTCue> create(ScriptExecutionContext&, const WebVTTCueData&);
 
-    static const AtomString& cueBackdropShadowPseudoId();
-
     virtual ~VTTCue();
 
     enum AutoKeyword {
@@ -123,7 +122,7 @@ public:
     const String& cueSettings() const { return m_settings; }
     void setCueSettings(const String&);
 
-    RefPtr<DocumentFragment> getCueAsHTML();
+    RefPtr<DocumentFragment> getCueAsHTML() final;
     RefPtr<DocumentFragment> createCueRenderingTree();
 
     const String& regionId() const { return m_regionId; }
@@ -133,11 +132,11 @@ public:
     void setIsActive(bool) override;
 
     bool hasDisplayTree() const { return m_displayTree; }
-    VTTCueBox& getDisplayTree(const IntSize& videoSize, int fontSize);
+    RefPtr<TextTrackCueBox> getDisplayTree(const IntSize&, int) final;
     HTMLSpanElement& element() const { return *m_cueHighlightBox; }
 
-    void updateDisplayTree(const MediaTime&);
-    void removeDisplayTree();
+    void updateDisplayTree(const MediaTime&) final;
+    void removeDisplayTree() final;
     void markFutureAndPastNodes(ContainerNode*, const MediaTime&, const MediaTime&);
 
     int calculateComputedLinePosition();
@@ -168,14 +167,15 @@ public:
     };
     CueAlignment getAlignment() const { return m_cueAlignment; }
 
-    virtual void setFontSize(int, const IntSize&, bool important);
+    void recalculateStyles() final { m_displayTreeShouldChange = true; }
+    void setFontSize(int, const IntSize&, bool important) override;
 
     bool isEqual(const TextTrackCue&, CueMatchRules) const override;
     bool cueContentsMatch(const TextTrackCue&) const override;
     bool doesExtendCue(const TextTrackCue&) const override;
 
     CueType cueType() const override { return WebVTT; }
-    bool isRenderable() const final { return true; }
+    bool isRenderable() const final { return !m_content.isEmpty(); }
 
     void didChange() override;
 
@@ -237,6 +237,9 @@ private:
 
     MediaTime m_originalStartTime;
 
+    int m_fontSize { 0 };
+    bool m_fontSizeIsImportant { false };
+
     bool m_snapToLines : 1;
     bool m_displayTreeShouldChange : 1;
     bool m_notifyRegion : 1;
@@ -263,7 +266,7 @@ struct LogArgument<WebCore::VTTCue> {
 } // namespace WTF
 
 SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::VTTCue)
-    static bool isType(const WebCore::TextTrackCue& cue) { return cue.isRenderable(); }
+static bool isType(const WebCore::TextTrackCue& cue) { return cue.cueType() == WebCore::TextTrackCue::WebVTT; }
 SPECIALIZE_TYPE_TRAITS_END()
 
 #endif
index 7607241..fe039cd 100644 (file)
@@ -281,7 +281,7 @@ const AtomString& VTTRegion::textTrackRegionShadowPseudoId()
     return trackRegionShadowPseudoId;
 }
 
-void VTTRegion::appendTextTrackCueBox(Ref<VTTCueBox>&& displayBox)
+void VTTRegion::appendTextTrackCueBox(Ref<TextTrackCueBox>&& displayBox)
 {
     ASSERT(m_cueContainer);
 
@@ -342,9 +342,13 @@ HTMLDivElement& VTTRegion::getDisplayTree()
 {
     if (!m_regionDisplayTree) {
         m_regionDisplayTree = HTMLDivElement::create(downcast<Document>(*m_scriptExecutionContext));
-        prepareRegionDisplayTree();
+        m_regionDisplayTree->setPseudo(textTrackRegionShadowPseudoId());
+        m_recalculateStyles = true;
     }
 
+    if (m_recalculateStyles)
+        prepareRegionDisplayTree();
+
     return *m_regionDisplayTree;
 }
 
@@ -383,14 +387,16 @@ void VTTRegion::prepareRegionDisplayTree()
 
     // The cue container is used to wrap the cues and it is the object which is
     // gradually scrolled out as multiple cues are appended to the region.
-    m_cueContainer = HTMLDivElement::create(downcast<Document>(*m_scriptExecutionContext));
+    if (!m_cueContainer) {
+        m_cueContainer = HTMLDivElement::create(downcast<Document>(*m_scriptExecutionContext));
+        m_cueContainer->setPseudo(textTrackCueContainerShadowPseudoId());
+        m_regionDisplayTree->appendChild(*m_cueContainer);
+    }
     m_cueContainer->setInlineStyleProperty(CSSPropertyTop, 0.0f, CSSPrimitiveValue::CSS_PX);
 
-    m_cueContainer->setPseudo(textTrackCueContainerShadowPseudoId());
-    m_regionDisplayTree->appendChild(*m_cueContainer);
-
     // 7.5 Every WebVTT region object is initialised with the following CSS
-    m_regionDisplayTree->setPseudo(textTrackRegionShadowPseudoId());
+
+    m_recalculateStyles = false;
 }
 
 void VTTRegion::startTimer()
index 6fcc4f3..d6762fc 100644 (file)
@@ -88,10 +88,12 @@ public:
 
     HTMLDivElement& getDisplayTree();
     
-    void appendTextTrackCueBox(Ref<VTTCueBox>&&);
+    void appendTextTrackCueBox(Ref<TextTrackCueBox>&&);
     void displayLastTextTrackCueBox();
     void willRemoveTextTrackCueBox(VTTCueBox*);
 
+    void cueStyleChanged() { m_recalculateStyles = true; }
+
 private:
     VTTRegion(ScriptExecutionContext&);
 
@@ -151,6 +153,8 @@ private:
     // currently it is used also for non-scrolling regions to use a single
     // code path.
     Timer m_scrollTimer;
+
+    bool m_recalculateStyles { true };
 };
 
 } // namespace WebCore
index 647ee56..8bdcc70 100644 (file)
@@ -587,7 +587,7 @@ String CaptionUserPreferencesMediaAF::captionsStyleSheetOverride() const
     String windowCornerRadius = windowRoundedCornerRadiusCSS();
     if (!windowColor.isEmpty() || !windowCornerRadius.isEmpty()) {
         captionsOverrideStyleSheet.appendLiteral(" ::");
-        captionsOverrideStyleSheet.append(VTTCue::cueBackdropShadowPseudoId());
+        captionsOverrideStyleSheet.append(TextTrackCue::cueBackdropShadowPseudoId());
         captionsOverrideStyleSheet.append('{');
         
         if (!windowColor.isEmpty())
index 03b75b0..ba2f456 100644 (file)
@@ -317,6 +317,9 @@ shouldDisplayCaptions:
 shouldDisplayTextDescriptions:
   initial: false
   conditional: VIDEO_TRACK
+genericCueAPIEnabled:
+    initial: false
+    conditional: VIDEO_TRACK
 scrollingCoordinatorEnabled:
   initial: false
 scrollingTreeIncludesFrames:
index fc68510..4cf8fa3 100644 (file)
@@ -43,8 +43,10 @@ WTF_MAKE_ISO_ALLOCATED_IMPL(RenderVTTCue);
 
 RenderVTTCue::RenderVTTCue(VTTCueBox& element, RenderStyle&& style)
     : RenderBlockFlow(element, WTFMove(style))
-    , m_cue(element.getCue())
+    , m_cue(toVTTCue(element.getCue()))
 {
+    ASSERT(m_cue);
+    ASSERT(is<VTTCue>(m_cue));
 }
 
 void RenderVTTCue::layout()
index 5136dae..a9e1a61 100644 (file)
@@ -1,3 +1,13 @@
+2019-11-01  Eric Carlson  <eric.carlson@apple.com>
+
+        Add experimental TextTrackCue API
+        https://bugs.webkit.org/show_bug.cgi?id=203649
+        <rdar://problem/55675172>
+
+        Reviewed by Jer Noble.
+
+        * Shared/WebPreferences.yaml:
+
 2019-11-01  Brian Burg  <bburg@apple.com>
 
         REGRESSION(r248696): Element Click on Mac is adding an extra page topContentInsets to y-coordinate
index 2de21c6..3448cdc 100644 (file)
@@ -1466,6 +1466,15 @@ CSSShadowPartsEnabled:
   category: experimental
   webcoreBinding: RuntimeEnabledFeatures
 
+GenericCueAPIEnabled:
+  type: bool
+  defaultValue: false
+  condition: ENABLE(VIDEO_TRACK)
+  humanReadableName: "Generic Text Track Cue API"
+  humanReadableDescription: "Enable Generic Text Track Cue API"
+  category: experimental
+  webcoreName: genericCueAPIEnabled
+
 # For internal features:
 # The type should be boolean.
 # You must provide a humanReadableName and humanReadableDescription for all debug features. They
index 1adcbde..0b249d6 100644 (file)
@@ -238,6 +238,15 @@ void InjectedBundle::overrideBoolPreferenceForTestRunner(WebPageGroupProxy* page
         RuntimeEnabledFeatures::sharedFeatures().setWebRTCMDNSICECandidatesEnabled(enabled);
 #endif
 
+#if ENABLE(VIDEO_TRACK)
+    if (preference == "WebKitGenericCueAPIEnabled") {
+        WebPreferencesStore::overrideBoolValueForKey(WebPreferencesKey::imageControlsEnabledKey(), enabled);
+        for (auto* page : pages)
+            page->settings().setGenericCueAPIEnabled(enabled);
+        return;
+    }
+#endif
+
     if (preference == "WebKitIsSecureContextAttributeEnabled") {
         WebPreferencesStore::overrideBoolValueForKey(WebPreferencesKey::isSecureContextAttributeEnabledKey(), enabled);
         RuntimeEnabledFeatures::sharedFeatures().setIsSecureContextAttributeEnabled(enabled);
index e695d83..6b5ad10 100644 (file)
 #define WebKitLegacyEncryptedMediaAPIEnabledKey @"WebKitLegacyEncryptedMediaAPIEnabled"
 #define WebKitEncryptedMediaAPIEnabledKey @"WebKitEncryptedMediaAPIEnabled"
 #define WebKitPictureInPictureAPIEnabledKey @"WebKitPictureInPictureAPIEnabled"
+#define WebKitGenericCueAPIEnabledKey @"WebKitGenericCueAPIEnabled"
 #define WebKitAllowMediaContentTypesRequiringHardwareSupportAsFallbackKey @"WebKitAllowMediaContentTypesRequiringHardwareSupportAsFallback"
 #define WebKitInspectorAdditionsEnabledPreferenceKey @"WebKitInspectorAdditionsEnabled"
 #define WebKitAccessibilityObjectModelEnabledPreferenceKey @"WebKitAccessibilityObjectModelEnabled"
index 49e43db..8366573 100644 (file)
@@ -613,6 +613,10 @@ public:
         @NO, WebKitPictureInPictureAPIEnabledKey,
 #endif
 
+#if ENABLE(VIDEO_TRACK)
+        @NO, WebKitGenericCueAPIEnabledKey,
+#endif
+
 #if ENABLE(MEDIA_STREAM)
         @NO, WebKitMockCaptureDevicesEnabledPreferenceKey,
         @YES, WebKitMockCaptureDevicesPromptEnabledPreferenceKey,
@@ -3344,6 +3348,16 @@ static NSString *classIBCreatorID = nil;
     [self _setBoolValue:flag forKey:WebKitPictureInPictureAPIEnabledKey];
 }
 
+- (BOOL)genericCueAPIEnabled
+{
+    return [self _boolValueForKey:WebKitGenericCueAPIEnabledKey];
+}
+
+- (void)setGenericCueAPIEnabled:(BOOL)flag
+{
+    [self _setBoolValue:flag forKey:WebKitGenericCueAPIEnabledKey];
+}
+
 - (BOOL)viewportFitEnabled
 {
     return [self _boolValueForKey:WebKitViewportFitEnabledPreferenceKey];
index ab1f1a1..81bdfe8 100644 (file)
@@ -645,6 +645,7 @@ extern NSString *WebPreferencesCacheModelChangedInternalNotification WEBKIT_DEPR
 @property (nonatomic) BOOL legacyEncryptedMediaAPIEnabled;
 @property (nonatomic) BOOL encryptedMediaAPIEnabled;
 @property (nonatomic) BOOL pictureInPictureAPIEnabled;
+@property (nonatomic) BOOL genericCueAPIEnabled;
 @property (nonatomic) BOOL viewportFitEnabled;
 @property (nonatomic) BOOL constantPropertiesEnabled;
 @property (nonatomic) BOOL colorFilterEnabled;
index 7d64476..cdff099 100644 (file)
@@ -3204,6 +3204,10 @@ static bool needsSelfRetainWhileLoadingQuirk()
     settings.setPictureInPictureAPIEnabled(preferences.pictureInPictureAPIEnabled);
 #endif
 
+#if ENABLE(VIDEO_TRACK)
+    settings.setGenericCueAPIEnabled(preferences.genericCueAPIEnabled);
+#endif
+
     RuntimeEnabledFeatures::sharedFeatures().setInspectorAdditionsEnabled(preferences.inspectorAdditionsEnabled);
 
     settings.setAllowMediaContentTypesRequiringHardwareSupportAsFallback(preferences.allowMediaContentTypesRequiringHardwareSupportAsFallback);
index 57692de..21e571e 100644 (file)
@@ -554,6 +554,7 @@ void InjectedBundle::beginTesting(WKDictionaryRef settings, BegingTestingMode te
 
     m_testRunner->setEncryptedMediaAPIEnabled(true);
     m_testRunner->setPictureInPictureAPIEnabled(true);
+    m_testRunner->setGenericCueAPIEnabled(false);
 
     m_testRunner->setCloseRemainingWindowsWhenComplete(false);
     m_testRunner->setAcceptsEditing(true);
index 0e727da..ed1933f 100644 (file)
@@ -490,6 +490,13 @@ void TestRunner::setPictureInPictureAPIEnabled(bool enabled)
     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), key.get(), enabled);
 }
 
+void TestRunner::setGenericCueAPIEnabled(bool enabled)
+{
+    WKRetainPtr<WKStringRef> key = adoptWK(WKStringCreateWithUTF8CString("WebKitGenericCueAPIEnabled"));
+    auto& injectedBundle = InjectedBundle::singleton();
+    WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), key.get(), enabled);
+}
+
 void TestRunner::setAllowsAnySSLCertificate(bool enabled)
 {
     auto& injectedBundle = InjectedBundle::singleton();
index 0e8fd64..cd9b71f 100644 (file)
@@ -130,6 +130,7 @@ public:
     void setShouldSwapToDefaultSessionOnNextNavigation(bool);
     void setEncryptedMediaAPIEnabled(bool);
     void setPictureInPictureAPIEnabled(bool);
+    void setGenericCueAPIEnabled(bool);
     void setMediaDevicesEnabled(bool);
     void setWebRTCMDNSICECandidatesEnabled(bool);
     void setCustomUserAgent(JSStringRef);