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/inspector/ConsoleMessage.h" 32 #include "core/page/Chrome.h" 33 #include "core/page/ChromeClient.h" 34 #include "core/page/Page.h" 35 #include "platform/PlatformMouseEvent.h" 36 37 namespace blink { 38 39 PointerLockController::PointerLockController(Page* page) 40 : m_page(page) 41 , m_lockPending(false) 42 { 43 } 44 45 PassOwnPtrWillBeRawPtr<PointerLockController> PointerLockController::create(Page* page) 46 { 47 return adoptPtrWillBeNoop(new PointerLockController(page)); 48 } 49 50 void PointerLockController::requestPointerLock(Element* target) 51 { 52 if (!target || !target->inDocument() || m_documentOfRemovedElementWhileWaitingForUnlock) { 53 enqueueEvent(EventTypeNames::pointerlockerror, 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(ConsoleMessage::create(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 return; 62 } 63 64 if (m_element) { 65 if (m_element->document() != target->document()) { 66 enqueueEvent(EventTypeNames::pointerlockerror, target); 67 return; 68 } 69 enqueueEvent(EventTypeNames::pointerlockchange, target); 70 m_element = target; 71 } else if (m_page->chrome().client().requestPointerLock()) { 72 m_lockPending = true; 73 m_element = target; 74 } else { 75 enqueueEvent(EventTypeNames::pointerlockerror, target); 76 } 77 } 78 79 void PointerLockController::requestPointerUnlock() 80 { 81 return m_page->chrome().client().requestPointerUnlock(); 82 } 83 84 void PointerLockController::elementRemoved(Element* element) 85 { 86 if (m_element == element) { 87 m_documentOfRemovedElementWhileWaitingForUnlock = &m_element->document(); 88 // Set element null immediately to block any future interaction with it 89 // including mouse events received before the unlock completes. 90 clearElement(); 91 requestPointerUnlock(); 92 } 93 } 94 95 void PointerLockController::documentDetached(Document* document) 96 { 97 if (m_element && m_element->document() == document) { 98 clearElement(); 99 requestPointerUnlock(); 100 } 101 } 102 103 bool PointerLockController::lockPending() const 104 { 105 return m_lockPending; 106 } 107 108 Element* PointerLockController::element() const 109 { 110 return m_element.get(); 111 } 112 113 void PointerLockController::didAcquirePointerLock() 114 { 115 enqueueEvent(EventTypeNames::pointerlockchange, m_element.get()); 116 m_lockPending = false; 117 } 118 119 void PointerLockController::didNotAcquirePointerLock() 120 { 121 enqueueEvent(EventTypeNames::pointerlockerror, m_element.get()); 122 clearElement(); 123 } 124 125 void PointerLockController::didLosePointerLock() 126 { 127 enqueueEvent(EventTypeNames::pointerlockchange, m_element ? &m_element->document() : m_documentOfRemovedElementWhileWaitingForUnlock.get()); 128 clearElement(); 129 m_documentOfRemovedElementWhileWaitingForUnlock = nullptr; 130 } 131 132 void PointerLockController::dispatchLockedMouseEvent(const PlatformMouseEvent& event, const AtomicString& eventType) 133 { 134 if (!m_element || !m_element->document().frame()) 135 return; 136 137 m_element->dispatchMouseEvent(event, eventType, event.clickCount()); 138 139 // Create click events 140 if (eventType == EventTypeNames::mouseup) 141 m_element->dispatchMouseEvent(event, EventTypeNames::click, event.clickCount()); 142 } 143 144 void PointerLockController::clearElement() 145 { 146 m_lockPending = false; 147 m_element = nullptr; 148 } 149 150 void PointerLockController::enqueueEvent(const AtomicString& type, Element* element) 151 { 152 if (element) 153 enqueueEvent(type, &element->document()); 154 } 155 156 void PointerLockController::enqueueEvent(const AtomicString& type, Document* document) 157 { 158 if (document && document->domWindow()) 159 document->domWindow()->enqueueDocumentEvent(Event::create(type)); 160 } 161 162 void PointerLockController::trace(Visitor* visitor) 163 { 164 visitor->trace(m_page); 165 visitor->trace(m_element); 166 visitor->trace(m_documentOfRemovedElementWhileWaitingForUnlock); 167 } 168 169 } // namespace blink 170