AX: Make video objects accessible on iOS
authorn_wang@apple.com <n_wang@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Oct 2017 23:43:13 +0000 (23:43 +0000)
committern_wang@apple.com <n_wang@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Oct 2017 23:43:13 +0000 (23:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=177788
<rdar://problem/34778028>

Reviewed by Chris Fleizach.

Source/WebCore:

Exposed certain <video> elements on iOS:
1. If they have no controls attribute set and have playsinline attribute set, that means
   normally there are custom controls provided.
2. Without autoplay attribute set. From iOS 10, with the motivation to improve performace by
   replacing GIF with <video>, elements will now honor the autoplay attribute if they
   have no audio. So normally those <video> elements are not interactive.

Also provided functions to let iOS users interact with the video elements:
- Play/Pause: accessibilityActivate
- Fast forward: accessibilityIncrement
- Rewind: accessibilityDecrement

Test: accessibility/ios-simulator/video-elements-ios.html

* WebCore.xcodeproj/project.pbxproj:
* accessibility/AXObjectCache.cpp:
(WebCore::createFromRenderer):
* accessibility/AccessibilityMediaObject.cpp: Added.
(WebCore::AccessibilityMediaObject::AccessibilityMediaObject):
(WebCore::AccessibilityMediaObject::~AccessibilityMediaObject):
(WebCore::AccessibilityMediaObject::create):
(WebCore::AccessibilityMediaObject::computeAccessibilityIsIgnored const):
(WebCore::AccessibilityMediaObject::mediaElement const):
(WebCore::AccessibilityMediaObject::stringValue const):
(WebCore::AccessibilityMediaObject::interactiveVideoDuration const):
(WebCore::AccessibilityMediaObject::mediaSeek):
(WebCore::AccessibilityMediaObject::toggleMute):
(WebCore::AccessibilityMediaObject::increment):
(WebCore::AccessibilityMediaObject::decrement):
(WebCore::AccessibilityMediaObject::press):
(WebCore::AccessibilityMediaObject::hasControlsAttributeSet const):
(WebCore::AccessibilityMediaObject::isPlaying const):
(WebCore::AccessibilityMediaObject::isMuted const):
(WebCore::AccessibilityMediaObject::isAutoplayEnabled const):
(WebCore::AccessibilityMediaObject::isPlayingInline const):
(WebCore::AccessibilityMediaObject::enterFullscreen const):
* accessibility/AccessibilityMediaObject.h: Added.
* accessibility/AccessibilityObject.cpp:
* accessibility/AccessibilityObject.h:
(WebCore::AccessibilityObject::isMediaObject const):
* accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
(-[WebAccessibilityObjectWrapper accessibilityIsWebInteractiveVideo]):
(-[WebAccessibilityObjectWrapper interactiveVideoDescription]):
(-[WebAccessibilityObjectWrapper accessibilityIsMediaPlaying]):
(-[WebAccessibilityObjectWrapper accessibilityIsMediaMuted]):
(-[WebAccessibilityObjectWrapper accessibilityToggleMuteForMedia]):
(-[WebAccessibilityObjectWrapper accessibilityVideoEnterFullscreen]):
(-[WebAccessibilityObjectWrapper determineIsAccessibilityElement]):
(-[WebAccessibilityObjectWrapper stringValueShouldBeUsedInLabel]):
(-[WebAccessibilityObjectWrapper accessibilityLabel]):

LayoutTests:

* accessibility/ios-simulator/video-elements-ios-expected.txt: Added.
* accessibility/ios-simulator/video-elements-ios.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/accessibility/ios-simulator/video-elements-ios-expected.txt [new file with mode: 0644]
LayoutTests/accessibility/ios-simulator/video-elements-ios.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/accessibility/AXObjectCache.cpp
Source/WebCore/accessibility/AccessibilityMediaObject.cpp [new file with mode: 0644]
Source/WebCore/accessibility/AccessibilityMediaObject.h [new file with mode: 0644]
Source/WebCore/accessibility/AccessibilityObject.cpp
Source/WebCore/accessibility/AccessibilityObject.h
Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm

index 8e3200f..8a38bdd 100644 (file)
@@ -1,3 +1,14 @@
+2017-10-04  Nan Wang  <n_wang@apple.com>
+
+        AX: Make video objects accessible on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=177788
+        <rdar://problem/34778028>
+
+        Reviewed by Chris Fleizach.
+
+        * accessibility/ios-simulator/video-elements-ios-expected.txt: Added.
+        * accessibility/ios-simulator/video-elements-ios.html: Added.
+
 2017-10-04  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Add basic support for the version of DataTransferItemList.add that takes a File
diff --git a/LayoutTests/accessibility/ios-simulator/video-elements-ios-expected.txt b/LayoutTests/accessibility/ios-simulator/video-elements-ios-expected.txt
new file mode 100644 (file)
index 0000000..59c4833
--- /dev/null
@@ -0,0 +1,20 @@
+  
+This tests the video elements on iOS.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS video2 is null
+AXLabel: Video label, 9 seconds
+AXValue: 0 seconds
+PASS video.description is 'AXLabel: Video label, 9 seconds'
+PASS video.stringValue is 'AXValue: 0 seconds'
+PASS vid.paused is true
+PASS vid.paused is false
+PASS vid.paused is true
+PASS video.stringValue is 'AXValue: 2 seconds'
+PASS video.stringValue is 'AXValue: 1 seconds'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/ios-simulator/video-elements-ios.html b/LayoutTests/accessibility/ios-simulator/video-elements-ios.html
new file mode 100644 (file)
index 0000000..6c05223
--- /dev/null
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body id="body">
+
+<video width="320" height="240" playsinline id="video" aria-label="Video label">
+  <source src="../../media/content/counting.mp4" type="video/mp4">
+  Your browser does not support the video tag.
+</video>
+
+<video width="320" height="240" controls id="video2" aria-label="Video label2">
+  <source src="../../media/content/counting.mp4" type="video/mp4">
+  Your browser does not support the video tag.
+</video>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+
+    description("This tests the video elements on iOS.");
+
+    if (window.accessibilityController) {
+        jsTestIsAsync = true;
+    
+        var vid = document.getElementById("video"); 
+        var video = accessibilityController.accessibleElementById("video");
+        
+        // We shouldn't expose video elements with native controls
+        var video2 = accessibilityController.accessibleElementById("video2");
+        shouldBeNull("video2");
+        
+        vid.oncanplaythrough = function() {
+            debug(video.description);
+            debug(video.stringValue);
+            shouldBe("video.description", "'AXLabel: Video label, 9 seconds'");
+            shouldBe("video.stringValue", "'AXValue: 0 seconds'");
+            
+            // Test play and pause.
+            shouldBeTrue("vid.paused");
+            video.press();
+            shouldBeFalse("vid.paused");
+            video.press();
+            shouldBeTrue("vid.paused");
+            
+            // Test fast forward and rewind.
+            video.increment();
+            video.increment();
+            shouldBe("video.stringValue", "'AXValue: 2 seconds'");
+            video.decrement();
+            shouldBe("video.stringValue", "'AXValue: 1 seconds'");
+            
+            finishJSTest();
+        };
+        
+    }
+    
+</script>
+
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index febdc79..4e93462 100644 (file)
@@ -1,3 +1,62 @@
+2017-10-04  Nan Wang  <n_wang@apple.com>
+
+        AX: Make video objects accessible on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=177788
+        <rdar://problem/34778028>
+
+        Reviewed by Chris Fleizach.
+
+        Exposed certain <video> elements on iOS:
+        1. If they have no controls attribute set and have playsinline attribute set, that means
+           normally there are custom controls provided. 
+        2. Without autoplay attribute set. From iOS 10, with the motivation to improve performace by 
+           replacing GIF with <video>, elements will now honor the autoplay attribute if they
+           have no audio. So normally those <video> elements are not interactive.
+
+        Also provided functions to let iOS users interact with the video elements:
+        - Play/Pause: accessibilityActivate
+        - Fast forward: accessibilityIncrement
+        - Rewind: accessibilityDecrement
+
+        Test: accessibility/ios-simulator/video-elements-ios.html
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::createFromRenderer):
+        * accessibility/AccessibilityMediaObject.cpp: Added.
+        (WebCore::AccessibilityMediaObject::AccessibilityMediaObject):
+        (WebCore::AccessibilityMediaObject::~AccessibilityMediaObject):
+        (WebCore::AccessibilityMediaObject::create):
+        (WebCore::AccessibilityMediaObject::computeAccessibilityIsIgnored const):
+        (WebCore::AccessibilityMediaObject::mediaElement const):
+        (WebCore::AccessibilityMediaObject::stringValue const):
+        (WebCore::AccessibilityMediaObject::interactiveVideoDuration const):
+        (WebCore::AccessibilityMediaObject::mediaSeek):
+        (WebCore::AccessibilityMediaObject::toggleMute):
+        (WebCore::AccessibilityMediaObject::increment):
+        (WebCore::AccessibilityMediaObject::decrement):
+        (WebCore::AccessibilityMediaObject::press):
+        (WebCore::AccessibilityMediaObject::hasControlsAttributeSet const):
+        (WebCore::AccessibilityMediaObject::isPlaying const):
+        (WebCore::AccessibilityMediaObject::isMuted const):
+        (WebCore::AccessibilityMediaObject::isAutoplayEnabled const):
+        (WebCore::AccessibilityMediaObject::isPlayingInline const):
+        (WebCore::AccessibilityMediaObject::enterFullscreen const):
+        * accessibility/AccessibilityMediaObject.h: Added.
+        * accessibility/AccessibilityObject.cpp:
+        * accessibility/AccessibilityObject.h:
+        (WebCore::AccessibilityObject::isMediaObject const):
+        * accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
+        (-[WebAccessibilityObjectWrapper accessibilityIsWebInteractiveVideo]):
+        (-[WebAccessibilityObjectWrapper interactiveVideoDescription]):
+        (-[WebAccessibilityObjectWrapper accessibilityIsMediaPlaying]):
+        (-[WebAccessibilityObjectWrapper accessibilityIsMediaMuted]):
+        (-[WebAccessibilityObjectWrapper accessibilityToggleMuteForMedia]):
+        (-[WebAccessibilityObjectWrapper accessibilityVideoEnterFullscreen]):
+        (-[WebAccessibilityObjectWrapper determineIsAccessibilityElement]):
+        (-[WebAccessibilityObjectWrapper stringValueShouldBeUsedInLabel]):
+        (-[WebAccessibilityObjectWrapper accessibilityLabel]):
+
 2017-10-04  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Add basic support for the version of DataTransferItemList.add that takes a File
index 3738a0e..80c21a9 100644 (file)
                A91C9FBF1B6586DE00AFFD54 /* AccessibilityTree.h in Headers */ = {isa = PBXBuildFile; fileRef = A91C9FBD1B6586DE00AFFD54 /* AccessibilityTree.h */; };
                A91C9FC21B659A6700AFFD54 /* AccessibilityTreeItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A91C9FC01B659A6700AFFD54 /* AccessibilityTreeItem.cpp */; };
                A91C9FC31B659A6700AFFD54 /* AccessibilityTreeItem.h in Headers */ = {isa = PBXBuildFile; fileRef = A91C9FC11B659A6700AFFD54 /* AccessibilityTreeItem.h */; };
+               A9787CB41F5F5C6600C551C6 /* AccessibilityMediaObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9787CB31F5F5C6500C551C6 /* AccessibilityMediaObject.cpp */; };
                A9C6E4E30D745E05006442E9 /* DOMMimeType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9C6E4E10D745E05006442E9 /* DOMMimeType.cpp */; };
                A9C6E4E40D745E05006442E9 /* DOMMimeType.h in Headers */ = {isa = PBXBuildFile; fileRef = A9C6E4E20D745E05006442E9 /* DOMMimeType.h */; };
                A9C6E4E70D745E18006442E9 /* DOMMimeTypeArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9C6E4E50D745E18006442E9 /* DOMMimeTypeArray.cpp */; };
                A91C9FBD1B6586DE00AFFD54 /* AccessibilityTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilityTree.h; sourceTree = "<group>"; };
                A91C9FC01B659A6700AFFD54 /* AccessibilityTreeItem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AccessibilityTreeItem.cpp; sourceTree = "<group>"; };
                A91C9FC11B659A6700AFFD54 /* AccessibilityTreeItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilityTreeItem.h; sourceTree = "<group>"; };
+               A9787CB21F5F599200C551C6 /* AccessibilityMediaObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AccessibilityMediaObject.h; sourceTree = "<group>"; };
+               A9787CB31F5F5C6500C551C6 /* AccessibilityMediaObject.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AccessibilityMediaObject.cpp; sourceTree = "<group>"; };
                A9C6E4E10D745E05006442E9 /* DOMMimeType.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DOMMimeType.cpp; sourceTree = "<group>"; };
                A9C6E4E20D745E05006442E9 /* DOMMimeType.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DOMMimeType.h; sourceTree = "<group>"; };
                A9C6E4E50D745E18006442E9 /* DOMMimeTypeArray.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DOMMimeTypeArray.cpp; sourceTree = "<group>"; };
                                07B0ABCE1032242200FBDC33 /* AccessibilityMathMLElement.h */,
                                2981CAA4131822EC00D12F2A /* AccessibilityMediaControls.cpp */,
                                07B0113E1032242200FBDC33 /* AccessibilityMediaControls.h */,
+                               A9787CB21F5F599200C551C6 /* AccessibilityMediaObject.h */,
+                               A9787CB31F5F5C6500C551C6 /* AccessibilityMediaObject.cpp */,
                                76CDD2EC1103DA6600680521 /* AccessibilityMenuList.cpp */,
                                76CDD2ED1103DA6600680521 /* AccessibilityMenuList.h */,
                                76CDD2F01103DA6600680521 /* AccessibilityMenuListOption.cpp */,
                                29A812480FBB9CA900510293 /* AccessibilityObjectMac.mm in Sources */,
                                37F57AC31A50728F00876F98 /* AccessibilityProgressIndicator.cpp in Sources */,
                                37F57AC41A50729300876F98 /* AccessibilityRenderObject.cpp in Sources */,
+                               A9787CB41F5F5C6600C551C6 /* AccessibilityMediaObject.cpp in Sources */,
                                37F57AC51A50729700876F98 /* AccessibilityScrollbar.cpp in Sources */,
                                37F57AC61A50729B00876F98 /* AccessibilityScrollView.cpp in Sources */,
                                37F57AC81A5072BC00876F98 /* AccessibilitySlider.cpp in Sources */,
index dfbb3b9..dee234a 100644 (file)
@@ -43,6 +43,7 @@
 #include "AccessibilityListBoxOption.h"
 #include "AccessibilityMathMLElement.h"
 #include "AccessibilityMediaControls.h"
+#include "AccessibilityMediaObject.h"
 #include "AccessibilityMenuList.h"
 #include "AccessibilityMenuListOption.h"
 #include "AccessibilityMenuListPopup.h"
@@ -444,6 +445,11 @@ static Ref<AccessibilityObject> createFromRenderer(RenderObject* renderer)
     if (node && is<HTMLLabelElement>(node) && nodeHasRole(node, nullAtom()))
         return AccessibilityLabel::create(renderer);
 
+#if PLATFORM(IOS)
+    if (is<HTMLMediaElement>(node) && nodeHasRole(node, nullAtom()))
+        return AccessibilityMediaObject::create(renderer);
+#endif
+
 #if ENABLE(VIDEO)
     // media controls
     if (node && node->isMediaControlElement())
diff --git a/Source/WebCore/accessibility/AccessibilityMediaObject.cpp b/Source/WebCore/accessibility/AccessibilityMediaObject.cpp
new file mode 100644 (file)
index 0000000..a70e8ab
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if PLATFORM(IOS)
+#include "AccessibilityMediaObject.h"
+
+#include "HTMLMediaElement.h"
+#include "HTMLNames.h"
+#include "HTMLVideoElement.h"
+#include "LocalizedStrings.h"
+
+
+namespace WebCore {
+    
+using namespace HTMLNames;
+
+AccessibilityMediaObject::AccessibilityMediaObject(RenderObject* renderer)
+    : AccessibilityRenderObject(renderer)
+{
+}
+
+AccessibilityMediaObject::~AccessibilityMediaObject()
+{
+}
+
+Ref<AccessibilityMediaObject> AccessibilityMediaObject::create(RenderObject* renderer)
+{
+    return adoptRef(*new AccessibilityMediaObject(renderer));
+}
+
+bool AccessibilityMediaObject::computeAccessibilityIsIgnored() const
+{
+    return accessibilityIsIgnoredByDefault();
+}
+
+HTMLMediaElement* AccessibilityMediaObject::mediaElement() const
+{
+    Node* node = this->node();
+    if (!is<HTMLMediaElement>(*node))
+        return nullptr;
+    return downcast<HTMLMediaElement>(node);
+}
+
+String AccessibilityMediaObject::stringValue() const
+{
+    if (HTMLMediaElement* element = mediaElement())
+        return localizedMediaTimeDescription(element->currentTime());
+    return AccessibilityRenderObject::stringValue();
+}
+
+String AccessibilityMediaObject::interactiveVideoDuration() const
+{
+    if (HTMLMediaElement* element = mediaElement())
+        return localizedMediaTimeDescription(element->duration());
+    return String();
+}
+    
+void AccessibilityMediaObject::mediaSeek(AXSeekDirection direction)
+{
+    HTMLMediaElement* element = mediaElement();
+    if (!element)
+        return;
+    
+    // Step 5% each time.
+    const double seekStep = .05;
+    double current = element->currentTime();
+    double duration = element->duration();
+    double timeDelta = ceil(duration * seekStep);
+
+    double time = direction == AXSeekForward ? std::min(current + timeDelta, duration) : std::max(current - timeDelta, 0.0);
+    element->setCurrentTime(time);
+}
+
+void AccessibilityMediaObject::toggleMute()
+{
+    HTMLMediaElement* element = mediaElement();
+    if (!element)
+        return;
+    
+    element->setMuted(!element->muted());
+}
+
+void AccessibilityMediaObject::increment()
+{
+    mediaSeek(AXSeekForward);
+}
+
+void AccessibilityMediaObject::decrement()
+{
+    mediaSeek(AXSeekBackward);
+}
+
+bool AccessibilityMediaObject::press()
+{
+    HTMLMediaElement* element = mediaElement();
+    if (!element)
+        return false;
+    
+    // We can safely call the internal togglePlayState method, which doesn't check restrictions,
+    // because this method is only called from user interaction.
+    element->togglePlayState();
+    return true;
+}
+
+bool AccessibilityMediaObject::hasControlsAttributeSet() const
+{
+    HTMLMediaElement* element = mediaElement();
+    if (!element)
+        return false;
+    
+    return element->controls();
+}
+    
+bool AccessibilityMediaObject::isPlaying() const
+{
+    HTMLMediaElement* element = mediaElement();
+    if (!element)
+        return false;
+    
+    return element->isPlaying();
+}
+
+bool AccessibilityMediaObject::isMuted() const
+{
+    HTMLMediaElement* element = mediaElement();
+    if (!element)
+        return false;
+    
+    return element->muted();
+}
+
+bool AccessibilityMediaObject::isAutoplayEnabled() const
+{
+    HTMLMediaElement* element = mediaElement();
+    if (!element)
+        return false;
+    
+    return element->autoplay();
+}
+
+bool AccessibilityMediaObject::isPlayingInline() const
+{
+    HTMLMediaElement* element = mediaElement();
+    if (!element)
+        return false;
+    
+    return !element->mediaSession().requiresFullscreenForVideoPlayback(*element);
+}
+
+void AccessibilityMediaObject::enterFullscreen() const
+{
+    Node* node = this->node();
+    if (!is<HTMLVideoElement>(node))
+        return;
+    
+    HTMLVideoElement* element = downcast<HTMLVideoElement>(node);
+    element->enterFullscreen();
+}
+    
+} // namespace WebCore
+
+#endif // PLATFORM(IOS)
diff --git a/Source/WebCore/accessibility/AccessibilityMediaObject.h b/Source/WebCore/accessibility/AccessibilityMediaObject.h
new file mode 100644 (file)
index 0000000..154a4d5
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if PLATFORM(IOS)
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+    
+class AccessibilityMediaObject final : public AccessibilityRenderObject {
+public:
+    static Ref<AccessibilityMediaObject> create(RenderObject*);
+    virtual ~AccessibilityMediaObject();
+    
+    void enterFullscreen() const;
+    void toggleMute();
+    
+    bool hasControlsAttributeSet() const;
+    String interactiveVideoDuration() const;
+    bool isPlaying() const;
+    bool isAutoplayEnabled() const;
+    bool isPlayingInline() const;
+    bool isMuted() const;
+
+private:
+    enum AXSeekDirection { AXSeekForward, AXSeekBackward };
+    explicit AccessibilityMediaObject(RenderObject*);
+    bool computeAccessibilityIsIgnored() const final;
+    bool isMediaObject() const final { return true; }
+    
+    String stringValue() const override;
+    bool press() override;
+    void increment() override;
+    void decrement() override;
+    
+    HTMLMediaElement* mediaElement() const;
+    
+    void mediaSeek(AXSeekDirection);
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityMediaObject, isMediaObject())
+#endif // PLATFORM(IOS)
index f0d5efb..9983ec1 100644 (file)
@@ -46,6 +46,7 @@
 #include "HTMLDetailsElement.h"
 #include "HTMLFormControlElement.h"
 #include "HTMLInputElement.h"
+#include "HTMLMediaElement.h"
 #include "HTMLNames.h"
 #include "HTMLParserIdioms.h"
 #include "HitTestResult.h"
index d64df67..371ba92 100644 (file)
@@ -563,6 +563,7 @@ public:
     virtual bool isSpinButtonPart() const { return false; }
     virtual bool isMockObject() const { return false; }
     virtual bool isMediaControlLabel() const { return false; }
+    virtual bool isMediaObject() const { return false; }
     bool isSwitch() const { return roleValue() == SwitchRole; }
     bool isToggleButton() const { return roleValue() == ToggleButtonRole; }
     bool isTextControl() const;
index 5feb354..e8a6464 100644 (file)
@@ -29,6 +29,7 @@
 #if HAVE(ACCESSIBILITY) && PLATFORM(IOS)
 
 #import "AccessibilityAttachment.h"
+#import "AccessibilityMediaObject.h"
 #import "AccessibilityRenderObject.h"
 #import "AccessibilityScrollView.h"
 #import "AccessibilityTable.h"
@@ -665,6 +666,70 @@ static AccessibilityObjectWrapper* AccessibilityUnignoredAncestor(AccessibilityO
     return traits;
 }
 
+- (BOOL)accessibilityIsWebInteractiveVideo
+{
+    if (![self _prepareAccessibilityCall])
+        return NO;
+    
+    // Only make the video object interactive if it plays inline and has no native controls.
+    if (m_object->roleValue() != VideoRole || !is<AccessibilityMediaObject>(m_object))
+        return NO;
+    
+    AccessibilityMediaObject* mediaObject = downcast<AccessibilityMediaObject>(m_object);
+    return !mediaObject->isAutoplayEnabled() && mediaObject->isPlayingInline() && !downcast<AccessibilityMediaObject>(m_object)->hasControlsAttributeSet();
+}
+
+- (NSString *)interactiveVideoDescription
+{
+    if (!is<AccessibilityMediaObject>(m_object))
+        return nil;
+    return downcast<AccessibilityMediaObject>(m_object)->interactiveVideoDuration();
+}
+
+- (BOOL)accessibilityIsMediaPlaying
+{
+    if (![self _prepareAccessibilityCall])
+        return NO;
+    
+    if (!is<AccessibilityMediaObject>(m_object))
+        return NO;
+    
+    return downcast<AccessibilityMediaObject>(m_object)->isPlaying();
+}
+
+- (BOOL)accessibilityIsMediaMuted
+{
+    if (![self _prepareAccessibilityCall])
+        return NO;
+    
+    if (!is<AccessibilityMediaObject>(m_object))
+        return NO;
+    
+    return downcast<AccessibilityMediaObject>(m_object)->isMuted();
+}
+
+- (void)accessibilityToggleMuteForMedia
+{
+    if (![self _prepareAccessibilityCall])
+        return;
+    
+    if (!is<AccessibilityMediaObject>(m_object))
+        return;
+
+    downcast<AccessibilityMediaObject>(m_object)->toggleMute();
+}
+
+- (void)accessibilityVideoEnterFullscreen
+{
+    if (![self _prepareAccessibilityCall])
+        return;
+    
+    if (!is<AccessibilityMediaObject>(m_object))
+        return;
+    
+    downcast<AccessibilityMediaObject>(m_object)->enterFullscreen();
+}
+
 - (uint64_t)_accessibilityTextEntryTraits
 {
     uint64_t traits = [self _axTextEntryTrait];
@@ -828,6 +893,9 @@ static AccessibilityObjectWrapper* AccessibilityUnignoredAncestor(AccessibilityO
                 return true;
             return false;
             
+        case VideoRole:
+            return [self accessibilityIsWebInteractiveVideo];
+            
         // Links can sometimes be elements (when they only contain static text or don't contain anything).
         // They should not be elements when containing text and other types.
         case WebCoreLinkRole:
@@ -944,7 +1012,6 @@ static AccessibilityObjectWrapper* AccessibilityUnignoredAncestor(AccessibilityO
         case ToolbarRole:
         case UnknownRole:
         case UserInterfaceTooltipRole:
-        case VideoRole:
         case WebApplicationRole:
         case WebAreaRole:
         case WindowRole:
@@ -975,6 +1042,8 @@ static AccessibilityObjectWrapper* AccessibilityUnignoredAncestor(AccessibilityO
         return NO;
     if (m_object->isFileUploadButton())
         return NO;
+    if ([self accessibilityIsWebInteractiveVideo])
+        return NO;
 
     return YES;
 }
@@ -1040,6 +1109,7 @@ static void appendStringToResult(NSMutableString *result, NSString *string)
     NSString *axTitle = [self baseAccessibilityTitle];
     NSString *axDescription = [self baseAccessibilityDescription];
     NSString *landmarkDescription = [self ariaLandmarkRoleDescription];
+    NSString *interactiveVideoDescription = [self interactiveVideoDescription];
     
     // We should expose the value of the input type date or time through AXValue instead of AXTitle.
     if (m_object->isInputTypePopupButton() && [axTitle isEqualToString:[self accessibilityValue]])
@@ -1061,6 +1131,7 @@ static void appendStringToResult(NSMutableString *result, NSString *string)
         appendStringToResult(result, valueLabel);
     }
     appendStringToResult(result, landmarkDescription);
+    appendStringToResult(result, interactiveVideoDescription);
     
     return [result length] ? result : nil;
 }