Commit fd0b8a9b authored by scheib@chromium.org's avatar scheib@chromium.org

Pointer Lock handles disconnected DOM elements

https://bugs.webkit.org/show_bug.cgi?id=77029

Reviewed by Adrienne Walker.

Source/WebCore:

Pointer Lock Controller now checks when elements or documents are
removed, and unlocks if the target element is being removed.

Tests: pointer-lock/locked-element-iframe-removed-from-dom.html
       pointer-lock/locked-element-removed-from-dom.html

* dom/Document.cpp:
(WebCore::Document::detach):
* dom/Element.cpp:
(WebCore::Element::removedFrom):
(WebCore::Element::webkitRequestPointerLock):
* page/PointerLockController.cpp:
(WebCore::PointerLockController::requestPointerLock):
(WebCore::PointerLockController::elementRemoved):
(WebCore):
(WebCore::PointerLockController::documentDetached):
(WebCore::PointerLockController::didLosePointerLock):
(WebCore::PointerLockController::enqueueEvent):
* page/PointerLockController.h:
(WebCore):
(PointerLockController):

LayoutTests:

Two new tests that verify pointer lock is released when the target
is removed from the document.

* pointer-lock/locked-element-iframe-removed-from-dom-expected.txt: Added.
* pointer-lock/locked-element-iframe-removed-from-dom.html: Added.
* pointer-lock/locked-element-removed-from-dom-expected.txt: Added.
* pointer-lock/locked-element-removed-from-dom.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@122626 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent c9c1237a
2012-07-13 Vincent Scheib <scheib@chromium.org>
Pointer Lock handles disconnected DOM elements
https://bugs.webkit.org/show_bug.cgi?id=77029
Reviewed by Adrienne Walker.
Two new tests that verify pointer lock is released when the target
is removed from the document.
* pointer-lock/locked-element-iframe-removed-from-dom-expected.txt: Added.
* pointer-lock/locked-element-iframe-removed-from-dom.html: Added.
* pointer-lock/locked-element-removed-from-dom-expected.txt: Added.
* pointer-lock/locked-element-removed-from-dom.html: Added.
2012-07-13 W. James MacLean <wjmaclean@chromium.org>
[chromium] Unreviewed gardening. storage/indexeddb/cursor-key-order.html has starting crashing on occassion.
Test removing an iframe containing a locked element causes lock to be released.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
Lock target in iframe. (main document handler)
Lock target in iframe. (iframe handler)
PASS onwebkitpointerlockchange received after: Lock target in iframe. (iframe handler)
PASS document.webkitPointerLockElement is targetDiv1
PASS targetDiv1.parentElement.parentElement is targetIframe1.contentDocument.body
Remove iframe & immediately lock target2. (main document handler)
Remove iframe & immediately lock target2. (iframe handler)
PASS document.webkitPointerLockElement is targetDiv2
PASS onwebkitpointerlockchange received after: Remove iframe & immediately lock target2. (main document handler)
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML>
<html>
<head>
<script src="../fast/js/resources/js-test-pre.js"></script>
<script src="resources/pointer-lock-test-harness.js"></script>
</head>
<body>
<div>
<iframe id="iframe1"></iframe>
<div id="target2"></div>
</div>
<script>
description("Test removing an iframe containing a locked element causes lock to be released.")
window.jsTestIsAsync = true;
targetIframe1 = document.getElementById("iframe1");
targetDiv2 = document.getElementById("target2");
todo = [
function () {
// Load a blank iframe.
targetIframe1.src = "about:blank";
targetIframe1.onload = function () { doNextStepWithUserGesture(); }
},
function () {
// Nest target element into iframe document.
targetIframe1.contentDocument.body.innerHTML ="<div><div></div></div>";
targetDiv1 = targetIframe1.contentDocument.body.firstChild.firstChild
expectNoEvents("Lock target in iframe. (main document handler)");
expectOnlyChangeEvent("Lock target in iframe. (iframe handler)", targetIframe1.contentDocument);
targetDiv1.webkitRequestPointerLock();
// doNextStep called by event handler.
},
function () {
shouldBe("document.webkitPointerLockElement", "targetDiv1");
shouldBe("targetDiv1.parentElement.parentElement", "targetIframe1.contentDocument.body");
expectOnlyChangeEvent("Remove iframe & immediately lock target2. (main document handler)");
expectNoEvents("Remove iframe & immediately lock target2. (iframe handler)", targetIframe1.contentDocument);
targetIframe1.parentElement.removeChild(targetIframe1);
targetDiv2.webkitRequestPointerLock();
shouldBe("document.webkitPointerLockElement", "targetDiv2");
// doNextStep called by event handler.
},
];
doNextStep();
</script>
<script src="../fast/js/resources/js-test-post.js"></script>
</body>
</html>
Test removing a locked element from a document causes lock to be released.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
Lock target in iframe. (main document handler).
Lock target in iframe. (iframe handler)
PASS onwebkitpointerlockchange received after: Lock target in iframe. (iframe handler)
PASS document.webkitPointerLockElement is targetDiv1
PASS targetDiv1.parentElement.parentElement is targetIframe1.contentDocument.body
Remove targetDiv1's parent from iframe & immediately lock target2. (main document handler)
Remove targetDiv1's parent from iframe & immediately lock target2. (iframe handler)
PASS document.webkitPointerLockElement is null
PASS targetDiv1.parentElement.parentElement is null
PASS onwebkitpointerlockerror received after: Remove targetDiv1's parent from iframe & immediately lock target2. (main document handler)
PASS onwebkitpointerlockchange received after: Remove targetDiv1's parent from iframe & immediately lock target2. (iframe handler)
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML>
<html>
<head>
<script src="../fast/js/resources/js-test-pre.js"></script>
<script src="resources/pointer-lock-test-harness.js"></script>
</head>
<body>
<div>
<iframe id="iframe1"></iframe>
<div id="target2"></div>
</div>
<script>
description("Test removing a locked element from a document causes lock to be released.")
window.jsTestIsAsync = true;
targetIframe1 = document.getElementById("iframe1");
targetDiv2 = document.getElementById("target2");
todo = [
function () {
// Load a blank iframe.
targetIframe1.src = "about:blank";
targetIframe1.onload = function () { doNextStepWithUserGesture(); }
},
function () {
// Nest target element into iframe document.
targetIframe1.contentDocument.body.innerHTML ="<div><div></div></div>";
targetDiv1 = targetIframe1.contentDocument.body.firstChild.firstChild
expectNoEvents("Lock target in iframe. (main document handler).");
expectOnlyChangeEvent("Lock target in iframe. (iframe handler)", targetIframe1.contentDocument);
targetDiv1.webkitRequestPointerLock();
// doNextStep called by event handler.
},
function () {
shouldBe("document.webkitPointerLockElement", "targetDiv1");
shouldBe("targetDiv1.parentElement.parentElement", "targetIframe1.contentDocument.body");
expectOnlyErrorEvent("Remove targetDiv1's parent from iframe & immediately lock target2. (main document handler)");
expectOnlyChangeEvent("Remove targetDiv1's parent from iframe & immediately lock target2. (iframe handler)", targetIframe1.contentDocument);
targetDiv1.parentElement.parentElement.removeChild(targetDiv1.parentElement);
targetDiv2.webkitRequestPointerLock();
shouldBe("document.webkitPointerLockElement", "null");
shouldBe("targetDiv1.parentElement.parentElement", "null");
// doNextStep called by event handler.
},
];
doNextStep();
</script>
<script src="../fast/js/resources/js-test-post.js"></script>
</body>
</html>
2012-07-13 Vincent Scheib <scheib@chromium.org>
Pointer Lock handles disconnected DOM elements
https://bugs.webkit.org/show_bug.cgi?id=77029
Reviewed by Adrienne Walker.
Pointer Lock Controller now checks when elements or documents are
removed, and unlocks if the target element is being removed.
Tests: pointer-lock/locked-element-iframe-removed-from-dom.html
pointer-lock/locked-element-removed-from-dom.html
* dom/Document.cpp:
(WebCore::Document::detach):
* dom/Element.cpp:
(WebCore::Element::removedFrom):
(WebCore::Element::webkitRequestPointerLock):
* page/PointerLockController.cpp:
(WebCore::PointerLockController::requestPointerLock):
(WebCore::PointerLockController::elementRemoved):
(WebCore):
(WebCore::PointerLockController::documentDetached):
(WebCore::PointerLockController::didLosePointerLock):
(WebCore::PointerLockController::enqueueEvent):
* page/PointerLockController.h:
(WebCore):
(PointerLockController):
2012-07-13 Ryosuke Niwa <rniwa@webkit.org>
HTMLCollection should use DynamicNodeList's invalidation model
......@@ -2080,6 +2080,11 @@ void Document::detach()
ASSERT(attached());
ASSERT(!m_inPageCache);
#if ENABLE(POINTER_LOCK)
if (page())
page()->pointerLockController()->documentDetached(this);
#endif
if (this == topDocument())
clearAXObjectCache();
......
......@@ -913,6 +913,10 @@ void Element::removedFrom(ContainerNode* insertionPoint)
if (containsFullScreenElement())
setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
#endif
#if ENABLE(POINTER_LOCK)
if (document()->page())
document()->page()->pointerLockController()->elementRemoved(this);
#endif
setSavedLayerScrollOffset(IntSize());
......@@ -1890,7 +1894,8 @@ void Element::setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(boo
#if ENABLE(POINTER_LOCK)
void Element::webkitRequestPointerLock()
{
document()->frame()->page()->pointerLockController()->requestPointerLock(this, 0, 0);
if (document()->page())
document()->page()->pointerLockController()->requestPointerLock(this, 0, 0);
}
#endif
......
......@@ -48,15 +48,12 @@ PassOwnPtr<PointerLockController> PointerLockController::create(Page* page)
void PointerLockController::requestPointerLock(Element* target, PassRefPtr<VoidCallback> successCallback, PassRefPtr<VoidCallback> failureCallback)
{
if (!target)
return;
if (!target->inDocument()) {
if (!target || !target->inDocument() || m_documentOfRemovedElementWhileWaitingForUnlock) {
enqueueEvent(eventNames().webkitpointerlockerrorEvent, target);
return;
}
if (isLocked()) {
if (m_element) {
// FIXME: Keep enqueueEvent usage. (https://bugs.webkit.org/show_bug.cgi?id=84402)
enqueueEvent(eventNames().webkitpointerlockchangeEvent, target);
if (m_element->document() != target->document())
......@@ -91,6 +88,25 @@ void PointerLockController::requestPointerUnlock()
return m_page->chrome()->client()->requestPointerUnlock();
}
void PointerLockController::elementRemoved(Element* element)
{
if (m_element == element) {
m_documentOfRemovedElementWhileWaitingForUnlock = m_element->document();
// Set element null immediately to block any future interaction with it
// including mouse events received before the unlock completes.
m_element = 0;
requestPointerUnlock();
}
}
void PointerLockController::documentDetached(Document* document)
{
if (m_element && m_element->document() == document) {
m_element = 0;
requestPointerUnlock();
}
}
bool PointerLockController::isLocked()
{
return m_page->chrome()->client()->isPointerLocked();
......@@ -136,11 +152,12 @@ void PointerLockController::didLosePointerLock(bool sendChangeEvent)
{
// FIXME: Keep enqueueEvent usage. (https://bugs.webkit.org/show_bug.cgi?id=84402)
if (sendChangeEvent)
enqueueEvent(eventNames().webkitpointerlockchangeEvent, m_element.get());
enqueueEvent(eventNames().webkitpointerlockchangeEvent, m_element ? m_element->document() : m_documentOfRemovedElementWhileWaitingForUnlock.get());
// FIXME: Remove callback usage. (https://bugs.webkit.org/show_bug.cgi?id=84402)
RefPtr<Element> elementToNotify(m_element);
m_element = 0;
m_documentOfRemovedElementWhileWaitingForUnlock = 0;
m_successCallback = 0;
m_failureCallback = 0;
if (elementToNotify && elementToNotify->document()->frame())
......@@ -161,9 +178,14 @@ void PointerLockController::dispatchLockedMouseEvent(const PlatformMouseEvent& e
void PointerLockController::enqueueEvent(const AtomicString& type, Element* element)
{
if (!element)
return;
element->document()->enqueueDocumentEvent(Event::create(type, true, false));
if (element)
enqueueEvent(type, element->document());
}
void PointerLockController::enqueueEvent(const AtomicString& type, Document* document)
{
if (document)
document->enqueueDocumentEvent(Event::create(type, true, false));
}
} // namespace WebCore
......
......@@ -33,6 +33,7 @@
namespace WebCore {
class Element;
class Document;
class Page;
class PlatformMouseEvent;
class VoidCallback;
......@@ -45,7 +46,9 @@ public:
void requestPointerLock(Element* target, PassRefPtr<VoidCallback> successCallback, PassRefPtr<VoidCallback> failureCallback);
void requestPointerUnlock();
bool isLocked();
void elementRemoved(Element*);
void documentDetached(Document*);
bool isLocked(); // FIXME: Rename to isClientLocked and move to private when removing old API. (https://bugs.webkit.org/show_bug.cgi?id=84402)
Element* element() const;
void didAcquirePointerLock();
......@@ -56,8 +59,10 @@ public:
private:
explicit PointerLockController(Page*);
void enqueueEvent(const AtomicString& type, Element*);
void enqueueEvent(const AtomicString& type, Document*);
Page* m_page;
RefPtr<Element> m_element;
RefPtr<Document> m_documentOfRemovedElementWhileWaitingForUnlock;
// FIXME: Remove callback usage. (https://bugs.webkit.org/show_bug.cgi?id=84402)
RefPtr<VoidCallback> m_successCallback;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment