Bug 1327097 - Part III, Trap mouse/touch/pointer events in audio/video element, r=smaug

This patch implements HTMLMediaElement::GetEventTargetParent and set
aVisitor.mCanHandle to false to mouse/touch/pointer events, when
the media control is present. This tells the event dispatcher that
these events are supposed to be handled exclusively by the
videocontrol binding within the media element, and should not
dispatch nor consumed by the content.

MozReview-Commit-ID: BXWZX9SYsuC

--HG--
extra : rebase_source : 5d6633a2e1a456d2d619b6f68498065d94c68c40
This commit is contained in:
Timothy Guan-tin Chien 2017-11-17 11:07:12 +08:00
Родитель 4c0b5b6275
Коммит 93fb9c8b32
5 изменённых файлов: 87 добавлений и 2 удалений

Просмотреть файл

@ -578,6 +578,12 @@ public:
{
return mIndeterminate;
}
bool IsDraggingRange() const
{
return mIsDraggingRange;
}
// XPCOM SetIndeterminate() is OK
void GetInputMode(nsAString& aValue);
@ -1535,7 +1541,7 @@ protected:
void ClearGetFilesHelpers();
/**
* nsINode::SetMayBeApzAware() will be invoked in this function if necessary
* nsINode::SetMayBeApzAware() will be invoked in this function if necessary
* to prevent default action of APZC so that we can increase/decrease the
* value of this InputElement when mouse wheel event comes without scrolling
* the page.

Просмотреть файл

@ -14,6 +14,7 @@
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/dom/MediaEncryptedEvent.h"
#include "mozilla/EMEUtils.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/Sprintf.h"
#include "base/basictypes.h"
@ -135,6 +136,7 @@ static mozilla::LazyLogModule gMediaElementEventsLog("nsMediaElementEvents");
#include "mozilla/EventStateManager.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/VideoPlaybackQuality.h"
#include "HTMLMediaElement.h"
@ -4392,6 +4394,60 @@ HTMLMediaElement::OutputMediaStream::~OutputMediaStream()
}
}
nsresult
HTMLMediaElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
{
if (!this->Controls() || !aVisitor.mEvent->mFlags.mIsTrusted) {
return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
}
HTMLInputElement* el = nullptr;
nsCOMPtr<nsINode> node;
// We will need to trap pointer, touch, and mouse events within the media
// element, allowing media control exclusive consumption on these events,
// and preventing the content from handling them.
switch (aVisitor.mEvent->mMessage) {
case ePointerDown:
case ePointerUp:
case eTouchEnd:
// Always prevent touchmove captured in video element from being handled by content,
// since we always do that for touchstart.
case eTouchMove:
case eTouchStart:
case eMouseClick:
case eMouseDoubleClick:
case eMouseDown:
case eMouseUp:
aVisitor.mCanHandle = false;
return NS_OK;
// The *move events however are only comsumed when the range input is being
// dragged.
case ePointerMove:
case eMouseMove:
node = do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
if (node->IsInNativeAnonymousSubtree()) {
if (node->IsHTMLElement(nsGkAtoms::input)) {
// The node is a <input type="range">
el = static_cast<HTMLInputElement*>(node.get());
} else if (node->GetParentNode() &&
node->GetParentNode()->IsHTMLElement(nsGkAtoms::input)) {
// The node is a child of <input type="range">
el = static_cast<HTMLInputElement*>(node->GetParentNode());
}
}
if (el && el->IsDraggingRange()) {
aVisitor.mCanHandle = false;
return NS_OK;
}
return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
default:
return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
}
}
bool HTMLMediaElement::ParseAttribute(int32_t aNamespaceID,
nsAtom* aAttribute,
const nsAString& aValue,

Просмотреть файл

@ -145,6 +145,10 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLMediaElement,
nsGenericHTMLElement)
// nsIDOMEventTarget
virtual nsresult
GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
virtual bool ParseAttribute(int32_t aNamespaceID,
nsAtom* aAttribute,
const nsAString& aValue,

Просмотреть файл

@ -98,7 +98,7 @@ waitUntilApzStable()
<body>
<p>Tap on the colored boxes to hide them.</p>
<video id="video"></video>
<audio id="audio" controls></audio>
<audio id="audio"></audio>
<div id="div"></div>
</body>
</html>

Просмотреть файл

@ -119,6 +119,16 @@ add_task(async function setup() {
video.addEventListener("seeked", verifyExpectedEvent);
document.addEventListener("mozfullscreenchange", verifyExpectedEvent);
["mousedown", "mouseup", "dblclick", "click"]
.forEach((eventType) => {
window.addEventListener(eventType, (evt) => {
// Prevent default action of leaked events and make the tests fail.
evt.preventDefault();
ok(false, "Event " + eventType + " in videocontrol should not leak to content;" +
"the event was dispatched from the " + evt.target.tagName.toLowerCase() + " element.");
});
});
// Check initial state upon load
is(video.paused, true, "checking video play state");
is(video.muted, false, "checking video mute state");
@ -212,6 +222,13 @@ add_task(async function drag_slider() {
const endDragX = scrubberOffsetX + (scrubberWidth / 2);
const expectedTime = videoDuration / 2;
function mousemoved(evt) {
ok(false, "Mousemove event should not be handled by content while dragging; " +
"the event was dispatched from the " + evt.target.tagName.toLowerCase() + " element.");
}
window.addEventListener("mousemove", mousemoved);
synthesizeMouse(video, beginDragX, scrubberCenterY, {type: "mousedown", button: 0});
synthesizeMouse(video, endDragX, scrubberCenterY, {type: "mousemove", button: 0});
synthesizeMouse(video, endDragX, scrubberCenterY, {type: "mouseup", button: 0});
@ -221,6 +238,8 @@ add_task(async function drag_slider() {
// in duration and position box. We can not precisely drag to expected position, so
// we just make sure the difference is within 10% of video duration.
ok(Math.abs(video.currentTime - expectedTime) < videoDuration / 10, "checking expected playback position");
window.removeEventListener("mousemove", mousemoved);
});
/*