Bug 1284788 - Allow change target of pointer lock when the pointer has been locked in the document. r=smaug

MozReview-Commit-ID: HiPkCPrQQr0

--HG--
extra : rebase_source : ce3826834065242a4e99b82d52ac65ddc205ae61
This commit is contained in:
Xidorn Quan 2016-07-20 14:42:08 +10:00
Родитель 90c16684c7
Коммит 834c029b00
5 изменённых файлов: 159 добавлений и 24 удалений

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

@ -12369,6 +12369,39 @@ GetPointerLockError(Element* aElement, Element* aCurrentLock,
return nullptr;
}
static void
ChangePointerLockedElement(Element* aElement, nsIDocument* aDocument,
Element* aPointerLockedElement)
{
// aDocument here is not really necessary, as it is the uncomposed
// document of both aElement and aPointerLockedElement as far as one
// is not nullptr, and they wouldn't both be nullptr in any case.
// But since the caller of this function should have known what the
// document is, we just don't try to figure out what it should be.
MOZ_ASSERT(aDocument);
MOZ_ASSERT(aElement != aPointerLockedElement);
if (aPointerLockedElement) {
MOZ_ASSERT(aPointerLockedElement->GetUncomposedDoc() == aDocument);
aPointerLockedElement->ClearPointerLock();
}
if (aElement) {
MOZ_ASSERT(aElement->GetUncomposedDoc() == aDocument);
aElement->SetPointerLock();
EventStateManager::sPointerLockedElement = do_GetWeakReference(aElement);
EventStateManager::sPointerLockedDoc = do_GetWeakReference(aDocument);
NS_ASSERTION(EventStateManager::sPointerLockedElement &&
EventStateManager::sPointerLockedDoc,
"aElement and this should support weak references!");
} else {
EventStateManager::sPointerLockedElement = nullptr;
EventStateManager::sPointerLockedDoc = nullptr;
}
// Retarget all events to aElement via capture or
// stop retargeting if aElement is nullptr.
nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK);
DispatchPointerLockChange(aDocument);
}
NS_IMETHODIMP
PointerLockRequest::Run()
{
@ -12390,6 +12423,12 @@ PointerLockRequest::Run()
}
// Note, we must bypass focus change, so pass true as the last parameter!
error = GetPointerLockError(e, pointerLockedElement, true);
// Another element in the same document is requesting pointer lock,
// just grant it without user input check.
if (!error && pointerLockedElement) {
ChangePointerLockedElement(e, d, pointerLockedElement);
return NS_OK;
}
}
// If it is neither user input initiated, nor requested in fullscreen,
// it should be rejected.
@ -12404,18 +12443,10 @@ PointerLockRequest::Run()
return NS_OK;
}
e->SetPointerLock();
EventStateManager::sPointerLockedElement = do_GetWeakReference(e);
EventStateManager::sPointerLockedDoc = do_GetWeakReference(doc);
NS_ASSERTION(EventStateManager::sPointerLockedElement &&
EventStateManager::sPointerLockedDoc,
"aElement and this should support weak references!");
ChangePointerLockedElement(e, d, nullptr);
nsContentUtils::DispatchEventOnlyToChrome(
doc, ToSupports(e), NS_LITERAL_STRING("MozDOMPointerLock:Entered"),
/* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
DispatchPointerLockChange(d);
return NS_OK;
}
@ -12507,19 +12538,12 @@ nsDocument::UnlockPointer(nsIDocument* aDoc)
nsCOMPtr<Element> pointerLockedElement =
do_QueryReferent(EventStateManager::sPointerLockedElement);
if (pointerLockedElement) {
pointerLockedElement->ClearPointerLock();
}
EventStateManager::sPointerLockedElement = nullptr;
EventStateManager::sPointerLockedDoc = nullptr;
ChangePointerLockedElement(nullptr, doc, pointerLockedElement);
nsContentUtils::DispatchEventOnlyToChrome(
doc, ToSupports(pointerLockedElement),
NS_LITERAL_STRING("MozDOMPointerLock:Exited"),
/* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
DispatchPointerLockChange(pointerLockedDoc);
}
void

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

@ -4346,9 +4346,6 @@ EventStateManager::SetPointerLock(nsIWidget* aWidget,
aWidget->SynthesizeNativeMouseMove(
sLastRefPoint + aWidget->WidgetToScreenOffset(), nullptr);
// Retarget all events to this element via capture.
nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK);
// Suppress DnD
if (dragService) {
dragService->Suppress();
@ -4367,9 +4364,6 @@ EventStateManager::SetPointerLock(nsIWidget* aWidget,
mPreLockPoint + aWidget->WidgetToScreenOffset(), nullptr);
}
// Don't retarget events to this element any more.
nsIPresShell::SetCapturingContent(nullptr, CAPTURE_POINTERLOCK);
// Unsuppress DnD
if (dragService) {
dragService->Unsuppress();

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

@ -0,0 +1,115 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bug 1284788</title>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="pointerlock_utils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<style>
#block1, #block2, #block3 {
background: blue;
width: 50px; height: 50px;
margin: 10px;
}
</style>
</head>
<body>
<div id="block1"></div>
<div id="block2"></div>
<div id="block3"></div>
<div id="test">
<script>
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("For changing pointer lock element not in a valid user event handler");
var block1 = document.getElementById("block1");
var block2 = document.getElementById("block2");
var block3 = document.getElementById("block3");
class ClickTester {
constructor(target) {
this._target = target;
this._callback = null;
document.addEventListener("click", this);
}
synthesize(callback) {
ok(!this._callback, "No callback should have been hooked");
this._callback = callback;
synthesizeMouseAtCenter(this._target, {}, window);
}
handleEvent(e) {
ok(!!this._callback, "Should have hooked a callback");
var callback = this._callback;
this._callback = null;
callback(e);
}
};
var tester = new ClickTester(block3);
// It would be called in handler of load event in pointerlock_utils.js
function start() {
tester.synthesize(firstClick);
}
function firstClick(e) {
is(e.target, block3, "Click is triggered inside block3");
document.addEventListener("mozpointerlockchange", lockedPointerOnBlock1);
block1.mozRequestPointerLock();
}
function lockedPointerOnBlock1() {
document.removeEventListener("mozpointerlockchange", lockedPointerOnBlock1);
is(document.mozPointerLockElement, block1, "Pointer should be locked on #block1");
SimpleTest.executeSoon(() => {
tester.synthesize(secondClick);
});
}
function secondClick(e) {
is(e.target, block1, "Event should be redirected to block1");
// Use 2s to ensure that we never consider this as an extension of user input.
setTimeout(() => {
document.addEventListener("mozpointerlockchange", lockedPointerOnBlock2);
block2.mozRequestPointerLock();
}, 2000);
}
function lockedPointerOnBlock2() {
document.removeEventListener("mozpointerlockchange", lockedPointerOnBlock2);
is(document.mozPointerLockElement, block2, "Pointer should be locked on #block2");
SimpleTest.executeSoon(() => {
tester.synthesize(thirdClick);
});
}
function thirdClick(e) {
is(e.target, block2, "Event should be redirected to block2");
// Use 2s to ensure that we never consider this as an extension of user input.
setTimeout(() => {
document.addEventListener("mozpointerlockchange", lockedPointerOnBlock1Again);
block1.mozRequestPointerLock();
}, 2000);
}
function lockedPointerOnBlock1Again() {
document.removeEventListener("mozpointerlockchange", lockedPointerOnBlock1Again);
is(document.mozPointerLockElement, block1, "Pointer should be locked on #block1 again");
SimpleTest.executeSoon(() => {
tester.synthesize(fourthClick);
});
}
function fourthClick(e) {
is(e.target, block1, "Event should be redirected to block1 again");
document.addEventListener("mozpointerlockchange", () => SimpleTest.finish());
document.mozExitPointerLock();
}
</script>
</div>
</body>
</html>

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

@ -18,6 +18,7 @@ support-files =
file_suppressSomeMouseEvents.html
file_locksvgelement.html
file_allowPointerLockSandboxFlag.html
file_changeLockElement.html
iframe_differentDOM.html
[test_pointerlock-api.html]

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

@ -56,7 +56,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
"file_suppressSomeMouseEvents.html",
"file_targetOutOfFocus.html",
"file_withoutDOM.html",
"file_allowPointerLockSandboxFlag.html"
"file_allowPointerLockSandboxFlag.html",
"file_changeLockElement.html",
];
var gDisableList = [