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 "PointerLockController.h" 27 28#include "Chrome.h" 29#include "ChromeClient.h" 30#include "Element.h" 31#include "Event.h" 32#include "Page.h" 33#include "PlatformMouseEvent.h" 34#include "VoidCallback.h" 35 36#if ENABLE(POINTER_LOCK) 37 38namespace WebCore { 39 40PointerLockController::PointerLockController(Page& page) 41 : m_page(page) 42 , m_lockPending(false) 43{ 44} 45 46void PointerLockController::requestPointerLock(Element* target) 47{ 48 if (!target || !target->inDocument() || m_documentOfRemovedElementWhileWaitingForUnlock) { 49 enqueueEvent(eventNames().pointerlockerrorEvent, target); 50 return; 51 } 52 53 if (target->document().isSandboxed(SandboxPointerLock)) { 54 // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists. 55 target->document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, ASCIILiteral("Blocked pointer lock on an element because the element's frame is sandboxed and the 'allow-pointer-lock' permission is not set.")); 56 enqueueEvent(eventNames().pointerlockerrorEvent, target); 57 return; 58 } 59 60 if (m_element) { 61 if (&m_element->document() != &target->document()) { 62 enqueueEvent(eventNames().pointerlockerrorEvent, target); 63 return; 64 } 65 enqueueEvent(eventNames().pointerlockchangeEvent, target); 66 m_element = target; 67 } else if (m_page.chrome().client().requestPointerLock()) { 68 m_lockPending = true; 69 m_element = target; 70 } else 71 enqueueEvent(eventNames().pointerlockerrorEvent, target); 72} 73 74void PointerLockController::requestPointerUnlock() 75{ 76 return m_page.chrome().client().requestPointerUnlock(); 77} 78 79void PointerLockController::elementRemoved(Element* element) 80{ 81 if (m_element == element) { 82 m_documentOfRemovedElementWhileWaitingForUnlock = &m_element->document(); 83 // Set element null immediately to block any future interaction with it 84 // including mouse events received before the unlock completes. 85 clearElement(); 86 requestPointerUnlock(); 87 } 88} 89 90void PointerLockController::documentDetached(Document* document) 91{ 92 if (m_element && &m_element->document() == document) { 93 clearElement(); 94 requestPointerUnlock(); 95 } 96} 97 98bool PointerLockController::lockPending() const 99{ 100 return m_lockPending; 101} 102 103Element* PointerLockController::element() const 104{ 105 return m_element.get(); 106} 107 108void PointerLockController::didAcquirePointerLock() 109{ 110 enqueueEvent(eventNames().pointerlockchangeEvent, m_element.get()); 111 m_lockPending = false; 112} 113 114void PointerLockController::didNotAcquirePointerLock() 115{ 116 enqueueEvent(eventNames().pointerlockerrorEvent, m_element.get()); 117 clearElement(); 118} 119 120void PointerLockController::didLosePointerLock() 121{ 122 enqueueEvent(eventNames().pointerlockchangeEvent, m_element ? &m_element->document() : m_documentOfRemovedElementWhileWaitingForUnlock.get()); 123 clearElement(); 124 m_documentOfRemovedElementWhileWaitingForUnlock = 0; 125} 126 127void PointerLockController::dispatchLockedMouseEvent(const PlatformMouseEvent& event, const AtomicString& eventType) 128{ 129 if (!m_element || !m_element->document().frame()) 130 return; 131 132 m_element->dispatchMouseEvent(event, eventType, event.clickCount()); 133 134 // Create click events 135 if (eventType == eventNames().mouseupEvent) 136 m_element->dispatchMouseEvent(event, eventNames().clickEvent, event.clickCount()); 137} 138 139void PointerLockController::clearElement() 140{ 141 m_lockPending = false; 142 m_element = 0; 143} 144 145void PointerLockController::enqueueEvent(const AtomicString& type, Element* element) 146{ 147 if (element) 148 enqueueEvent(type, &element->document()); 149} 150 151void PointerLockController::enqueueEvent(const AtomicString& type, Document* document) 152{ 153 if (document) 154 document->enqueueDocumentEvent(Event::create(type, true, false)); 155} 156 157} // namespace WebCore 158 159#endif // ENABLE(POINTER_LOCK) 160