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
diff --git a/Source/WebCore/page/PointerLockController.cpp b/Source/WebCore/page/PointerLockController.cpp
index c250cb7..abbfc80 100644
--- a/Source/WebCore/page/PointerLockController.cpp
+++ b/Source/WebCore/page/PointerLockController.cpp
@@ -48,15 +48,12 @@
 
 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 @@
     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 @@
 {
     // 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::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