Home | History | Annotate | Download | only in page
      1 /*
      2  * Copyright (C) 2012 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1.  Redistributions of source code must retain the above copyright
      8  *     notice, this list of conditions and the following disclaimer.
      9  * 2.  Redistributions in binary form must reproduce the above copyright
     10  *     notice, this list of conditions and the following disclaimer in the
     11  *     documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
     20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     23  */
     24 
     25 #include "config.h"
     26 #include "core/page/PointerLockController.h"
     27 
     28 #include "core/dom/Element.h"
     29 #include "core/events/Event.h"
     30 #include "core/frame/LocalDOMWindow.h"
     31 #include "core/page/Chrome.h"
     32 #include "core/page/ChromeClient.h"
     33 #include "core/page/Page.h"
     34 #include "platform/PlatformMouseEvent.h"
     35 
     36 namespace WebCore {
     37 
     38 PointerLockController::PointerLockController(Page* page)
     39     : m_page(page)
     40     , m_lockPending(false)
     41 {
     42 }
     43 
     44 PassOwnPtrWillBeRawPtr<PointerLockController> PointerLockController::create(Page* page)
     45 {
     46     return adoptPtrWillBeNoop(new PointerLockController(page));
     47 }
     48 
     49 void PointerLockController::requestPointerLock(Element* target)
     50 {
     51     if (!target || !target->inDocument() || m_documentOfRemovedElementWhileWaitingForUnlock) {
     52         enqueueEvent(EventTypeNames::pointerlockerror, target);
     53         enqueueEvent(EventTypeNames::webkitpointerlockerror, target);
     54         return;
     55     }
     56 
     57     if (target->document().isSandboxed(SandboxPointerLock)) {
     58         // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
     59         target->document().addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Blocked pointer lock on an element because the element's frame is sandboxed and the 'allow-pointer-lock' permission is not set.");
     60         enqueueEvent(EventTypeNames::pointerlockerror, target);
     61         enqueueEvent(EventTypeNames::webkitpointerlockerror, target);
     62         return;
     63     }
     64 
     65     if (m_element) {
     66         if (m_element->document() != target->document()) {
     67             enqueueEvent(EventTypeNames::pointerlockerror, target);
     68             enqueueEvent(EventTypeNames::webkitpointerlockerror, target);
     69             return;
     70         }
     71         enqueueEvent(EventTypeNames::pointerlockchange, target);
     72         enqueueEvent(EventTypeNames::webkitpointerlockchange, target);
     73         m_element = target;
     74     } else if (m_page->chrome().client().requestPointerLock()) {
     75         m_lockPending = true;
     76         m_element = target;
     77     } else {
     78         enqueueEvent(EventTypeNames::pointerlockerror, target);
     79         enqueueEvent(EventTypeNames::webkitpointerlockerror, target);
     80     }
     81 }
     82 
     83 void PointerLockController::requestPointerUnlock()
     84 {
     85     return m_page->chrome().client().requestPointerUnlock();
     86 }
     87 
     88 void PointerLockController::elementRemoved(Element* element)
     89 {
     90     if (m_element == element) {
     91         m_documentOfRemovedElementWhileWaitingForUnlock = &m_element->document();
     92         // Set element null immediately to block any future interaction with it
     93         // including mouse events received before the unlock completes.
     94         clearElement();
     95         requestPointerUnlock();
     96     }
     97 }
     98 
     99 void PointerLockController::documentDetached(Document* document)
    100 {
    101     if (m_element && m_element->document() == document) {
    102         clearElement();
    103         requestPointerUnlock();
    104     }
    105 }
    106 
    107 bool PointerLockController::lockPending() const
    108 {
    109     return m_lockPending;
    110 }
    111 
    112 Element* PointerLockController::element() const
    113 {
    114     return m_element.get();
    115 }
    116 
    117 void PointerLockController::didAcquirePointerLock()
    118 {
    119     enqueueEvent(EventTypeNames::pointerlockchange, m_element.get());
    120     enqueueEvent(EventTypeNames::webkitpointerlockchange, m_element.get());
    121     m_lockPending = false;
    122 }
    123 
    124 void PointerLockController::didNotAcquirePointerLock()
    125 {
    126     enqueueEvent(EventTypeNames::pointerlockerror, m_element.get());
    127     enqueueEvent(EventTypeNames::webkitpointerlockerror, m_element.get());
    128     clearElement();
    129 }
    130 
    131 void PointerLockController::didLosePointerLock()
    132 {
    133     enqueueEvent(EventTypeNames::pointerlockchange, m_element ? &m_element->document() : m_documentOfRemovedElementWhileWaitingForUnlock.get());
    134     enqueueEvent(EventTypeNames::webkitpointerlockchange, m_element ? &m_element->document() : m_documentOfRemovedElementWhileWaitingForUnlock.get());
    135     clearElement();
    136     m_documentOfRemovedElementWhileWaitingForUnlock = nullptr;
    137 }
    138 
    139 void PointerLockController::dispatchLockedMouseEvent(const PlatformMouseEvent& event, const AtomicString& eventType)
    140 {
    141     if (!m_element || !m_element->document().frame())
    142         return;
    143 
    144     m_element->dispatchMouseEvent(event, eventType, event.clickCount());
    145 
    146     // Create click events
    147     if (eventType == EventTypeNames::mouseup)
    148         m_element->dispatchMouseEvent(event, EventTypeNames::click, event.clickCount());
    149 }
    150 
    151 void PointerLockController::clearElement()
    152 {
    153     m_lockPending = false;
    154     m_element = nullptr;
    155 }
    156 
    157 void PointerLockController::enqueueEvent(const AtomicString& type, Element* element)
    158 {
    159     if (element)
    160         enqueueEvent(type, &element->document());
    161 }
    162 
    163 void PointerLockController::enqueueEvent(const AtomicString& type, Document* document)
    164 {
    165     if (document && document->domWindow())
    166         document->domWindow()->enqueueDocumentEvent(Event::create(type));
    167 }
    168 
    169 void PointerLockController::trace(Visitor* visitor)
    170 {
    171     visitor->trace(m_page);
    172     visitor->trace(m_element);
    173     visitor->trace(m_documentOfRemovedElementWhileWaitingForUnlock);
    174 }
    175 
    176 } // namespace WebCore
    177