1 /* 2 * Copyright (C) 2010 Apple 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'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "platform/UserGestureIndicator.h" 28 29 #include "wtf/Assertions.h" 30 #include "wtf/CurrentTime.h" 31 #include "wtf/MainThread.h" 32 33 namespace blink { 34 35 namespace { 36 37 // User gestures timeout in 1 second. 38 const double userGestureTimeout = 1.0; 39 40 // For out of process tokens we allow a 10 second delay. 41 const double userGestureOutOfProcessTimeout = 10.0; 42 43 class GestureToken : public UserGestureToken { 44 public: 45 static PassRefPtr<UserGestureToken> create() { return adoptRef(new GestureToken); } 46 47 virtual ~GestureToken() { } 48 virtual bool hasGestures() const OVERRIDE 49 { 50 // Do not enforce timeouts for gestures which spawned javascript prompts. 51 if (m_consumableGestures < 1 || (WTF::currentTime() - m_timestamp > (m_outOfProcess ? userGestureOutOfProcessTimeout : userGestureTimeout) && !m_javascriptPrompt)) 52 return false; 53 return true; 54 } 55 56 void addGesture() 57 { 58 m_consumableGestures++; 59 m_timestamp = WTF::currentTime(); 60 } 61 62 void resetTimestamp() 63 { 64 m_timestamp = WTF::currentTime(); 65 } 66 67 bool consumeGesture() 68 { 69 if (!m_consumableGestures) 70 return false; 71 m_consumableGestures--; 72 return true; 73 } 74 75 virtual void setOutOfProcess() OVERRIDE 76 { 77 if (WTF::currentTime() - m_timestamp > userGestureTimeout) 78 return; 79 if (hasGestures()) 80 m_outOfProcess = true; 81 } 82 83 virtual void setJavascriptPrompt() OVERRIDE 84 { 85 if (WTF::currentTime() - m_timestamp > userGestureTimeout) 86 return; 87 if (hasGestures()) 88 m_javascriptPrompt = true; 89 } 90 91 private: 92 GestureToken() 93 : m_consumableGestures(0) 94 , m_timestamp(0) 95 , m_outOfProcess(false) 96 , m_javascriptPrompt(false) 97 { 98 } 99 100 size_t m_consumableGestures; 101 double m_timestamp; 102 bool m_outOfProcess; 103 bool m_javascriptPrompt; 104 }; 105 106 } 107 108 static bool isDefinite(ProcessingUserGestureState state) 109 { 110 return state == DefinitelyProcessingNewUserGesture || state == DefinitelyProcessingUserGesture || state == DefinitelyNotProcessingUserGesture; 111 } 112 113 ProcessingUserGestureState UserGestureIndicator::s_state = DefinitelyNotProcessingUserGesture; 114 UserGestureIndicator* UserGestureIndicator::s_topmostIndicator = 0; 115 bool UserGestureIndicator::s_processedUserGestureSinceLoad = false; 116 117 UserGestureIndicator::UserGestureIndicator(ProcessingUserGestureState state) 118 : m_previousState(s_state) 119 { 120 // Silently ignore UserGestureIndicators on non-main threads. 121 if (!isMainThread()) 122 return; 123 124 // We overwrite s_state only if the caller is definite about the gesture state. 125 if (isDefinite(state)) { 126 if (!s_topmostIndicator) { 127 s_topmostIndicator = this; 128 m_token = GestureToken::create(); 129 } else { 130 m_token = s_topmostIndicator->currentToken(); 131 } 132 s_state = state; 133 } 134 135 if (state == DefinitelyProcessingNewUserGesture) { 136 static_cast<GestureToken*>(m_token.get())->addGesture(); 137 s_processedUserGestureSinceLoad = true; 138 } else if (state == DefinitelyProcessingUserGesture && s_topmostIndicator == this) { 139 static_cast<GestureToken*>(m_token.get())->addGesture(); 140 s_processedUserGestureSinceLoad = true; 141 } 142 ASSERT(isDefinite(s_state)); 143 } 144 145 UserGestureIndicator::UserGestureIndicator(PassRefPtr<UserGestureToken> token) 146 : m_previousState(s_state) 147 { 148 // Silently ignore UserGestureIndicators on non-main threads. 149 if (!isMainThread()) 150 return; 151 152 if (token) { 153 static_cast<GestureToken*>(token.get())->resetTimestamp(); 154 if (!s_topmostIndicator) { 155 s_topmostIndicator = this; 156 m_token = token; 157 } else { 158 m_token = s_topmostIndicator->currentToken(); 159 if (static_cast<GestureToken*>(token.get())->hasGestures()) { 160 static_cast<GestureToken*>(m_token.get())->addGesture(); 161 static_cast<GestureToken*>(token.get())->consumeGesture(); 162 } 163 } 164 s_state = DefinitelyProcessingUserGesture; 165 } 166 167 ASSERT(isDefinite(s_state)); 168 } 169 170 UserGestureIndicator::~UserGestureIndicator() 171 { 172 if (!isMainThread()) 173 return; 174 s_state = m_previousState; 175 if (s_topmostIndicator == this) 176 s_topmostIndicator = 0; 177 ASSERT(isDefinite(s_state)); 178 } 179 180 bool UserGestureIndicator::processingUserGesture() 181 { 182 if (!isMainThread()) 183 return false; 184 return s_topmostIndicator && static_cast<GestureToken*>(s_topmostIndicator->currentToken())->hasGestures() && (s_state == DefinitelyProcessingNewUserGesture || s_state == DefinitelyProcessingUserGesture); 185 } 186 187 bool UserGestureIndicator::consumeUserGesture() 188 { 189 if (!isMainThread() || !s_topmostIndicator) 190 return false; 191 return static_cast<GestureToken*>(s_topmostIndicator->currentToken())->consumeGesture(); 192 } 193 194 UserGestureToken* UserGestureIndicator::currentToken() 195 { 196 if (!isMainThread() || !s_topmostIndicator) 197 return 0; 198 return s_topmostIndicator->m_token.get(); 199 } 200 201 void UserGestureIndicator::clearProcessedUserGestureSinceLoad() 202 { 203 if (isMainThread()) 204 s_processedUserGestureSinceLoad = false; 205 } 206 207 bool UserGestureIndicator::processedUserGestureSinceLoad() 208 { 209 if (!isMainThread()) 210 return false; 211 return s_processedUserGestureSinceLoad; 212 } 213 214 UserGestureIndicatorDisabler::UserGestureIndicatorDisabler() 215 : m_savedState(UserGestureIndicator::s_state) 216 , m_savedIndicator(UserGestureIndicator::s_topmostIndicator) 217 { 218 RELEASE_ASSERT(isMainThread()); 219 UserGestureIndicator::s_state = DefinitelyNotProcessingUserGesture; 220 UserGestureIndicator::s_topmostIndicator = 0; 221 } 222 223 UserGestureIndicatorDisabler::~UserGestureIndicatorDisabler() 224 { 225 RELEASE_ASSERT(isMainThread()); 226 UserGestureIndicator::s_state = m_savedState; 227 UserGestureIndicator::s_topmostIndicator = m_savedIndicator; 228 } 229 230 } 231