Changeset 167579 in webkit
- Timestamp:
- Apr 20, 2014 9:39:11 PM (10 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r167578 r167579 1 2014-04-20 Darin Adler <darin@apple.com> 2 3 ScriptExecutionContext::stopActiveDOMObjects iterates a hash map that can change during iteration (for multiple reasons, including GC) 4 https://bugs.webkit.org/show_bug.cgi?id=52719 5 6 Reviewed by Alexey Proskuryakov. 7 8 At least two specific ways this can happen: 9 10 1) XMLHttpRequest::stop can trigger a JavaScript garbage collection. 11 2) NotificationCenter::stop can delete the last references to notifications; 12 those notifications are also active DOM objects. 13 14 Besides fixing the iteration in that function, did some other fixes for the 15 ScriptExecutionContext class, including some coding style changes. Many uses 16 of nullptr instead of 0, without listing each function separately below. 17 18 * Modules/webdatabase/DatabaseContext.cpp: 19 (WebCore::DatabaseContext::contextDestroyed): Call through to the base class 20 version of contextDestroyed rather than repeating what it does (with a large 21 comment that doesn't acknowledge the base class alread does it). 22 * Modules/webdatabase/DatabaseContext.h: Removed some unneeded includes. 23 Wrote out "private" explicitly for deriving from ActiveDOMObject. Made the 24 ActiveDOMObject function overrides private, and marked them override and final. 25 26 * dom/ActiveDOMObject.h: Updated comments. Replaced suspendIfNeededCalled with 27 assertSuspendIfNeededWasCalled, which has an empty inline version in the header. 28 Renamed m_suspendIfNeededCalled to m_suspendIfNeededWasCalled. 29 30 * dom/ActiveDOMObject.cpp: 31 (WebCore::ActiveDOMObject::ActiveDOMObject): Pass a reference instead of a pointer. 32 (WebCore::ActiveDOMObject::~ActiveDOMObject): Ditto. 33 (WebCore::ActiveDOMObject::suspendIfNeeded): Ditto. 34 35 * dom/ContextDestructionObserver.cpp: 36 (WebCore::ContextDestructionObserver::observeContext): Pass a reference instead of a pointer. 37 38 * dom/MessagePort.cpp: 39 (WebCore::MessagePort::MessagePort): Pass a reference instead of a pointer. 40 (WebCore::MessagePort::~MessagePort): Ditto. 41 (WebCore::MessagePort::disentangle): Ditto. 42 43 * dom/ScriptExecutionContext.cpp: 44 (WebCore::ScriptExecutionContext::ScriptExecutionContext): Updated flags used 45 for assertions so they are conditional and updated their names. 46 (WebCore::takeAny): Added. Helper function that we can consider for HashSet in 47 the future; makes loop below easier to read. 48 (WebCore::checkConsistency): Added. Assertions that were done multiple places below, 49 and should not be written over and over again. 50 (WebCore::ScriptExecutionContext::~ScriptExecutionContext): Changed to use C++11 51 for loops and the takeAny function above. 52 (WebCore::ScriptExecutionContext::dispatchMessagePortEvents): Ditto. 53 (WebCore::ScriptExecutionContext::createdMessagePort): Changed to take a reference 54 for clarity and so it doesn't have to do an assert the pointer is non-null. 55 (WebCore::ScriptExecutionContext::destroyedMessagePort): Ditto. 56 (WebCore::ScriptExecutionContext::canSuspendActiveDOMObjects): Changed to use 57 C++11 for loop and reworded comment and redid assertions. 58 (WebCore::ScriptExecutionContext::suspendActiveDOMObjects): Ditto. 59 (WebCore::ScriptExecutionContext::resumeActiveDOMObjects): Ditto. 60 (WebCore::ScriptExecutionContext::stopActiveDOMObjects): Changed to support 61 removal of an active DOM object during the stop function. Included new comments 62 to clarify what the rules are. 63 (WebCore::ScriptExecutionContext::suspendActiveDOMObjectIfNeeded): Changed to take 64 a reference for clarity and so it doesn't have to assert a pointer is non-null. 65 (WebCore::ScriptExecutionContext::didCreateActiveDOMObject): Ditto. Also changed to 66 use RELEASE_ASSERT instead of CRASH. 67 (WebCore::ScriptExecutionContext::willDestroyActiveDOMObject): Ditto. 68 (WebCore::ScriptExecutionContext::didCreateDestructionObserver): Ditto. 69 (WebCore::ScriptExecutionContext::willDestroyDestructionObserver): Ditto. 70 (WebCore::ScriptExecutionContext::closeMessagePorts): Moved the body of this 71 function into its one call site, ScriptExecutionContext::stopActiveDOMObjects, 72 since it's simple enough when written as a C++11 for loop. 73 (WebCore::ScriptExecutionContext::hasPendingActivity): Added. This function was 74 already exported for workers, and implementing it outside this class required 75 exposing the private HashSet members; more sensible to implement it here and 76 simply make it public in WorkerGlobalScope. 77 78 * dom/ScriptExecutionContext.h: Removed unnecessary includes and forward declarations. 79 Removed a long-ago-fixed FIXME. Changed various functions to take references instead of 80 pointers. Added a protected hasPendingActivity function, deleted the closeMessagePorts 81 function, deleted the ActiveDOMObjectsSet typedef, made the assertion flags be 82 !ASSERT_DISABLED only, and deleted the messagePorts and activeDOMObjects functions. 83 84 * workers/WorkerGlobalScope.cpp: 85 (WebCore::WorkerGlobalScope::hasPendingActivity): Deleted. This is now implemented 86 in the base class. 87 88 * workers/WorkerGlobalScope.h: Make hasPendingActivity function from the base class 89 public instead of declaring it in this class. 90 1 91 2014-04-20 Brent Fulgham <bfulgham@apple.com> 2 92 -
trunk/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp
r166661 r167579 105 105 #if PLATFORM(IOS) 106 106 , m_paused(false) 107 #endif //PLATFORM(IOS)107 #endif 108 108 { 109 109 // ActiveDOMObject expects this to be called to set internal flags. … … 129 129 } 130 130 131 // This is called if the associated ScriptExecutionContext is destr uctingwhile131 // This is called if the associated ScriptExecutionContext is destroyed while 132 132 // we're still associated with it. That's our cue to disassociate and shutdown. 133 // To do this, we stop the database and let everything shut down naturally134 // because the database closing process m aystill make use of this context.133 // To do this, we stop the database and let everything shut down naturally 134 // because the database closing process might still make use of this context. 135 135 // It is not safe to just delete the context here. 136 136 void DatabaseContext::contextDestroyed() 137 137 { 138 138 stopDatabases(); 139 140 // Normally, willDestroyActiveDOMObject() is called in ~ActiveDOMObject(). 141 // However, we're here because the destructor hasn't been called, and the 142 // ScriptExecutionContext we're associated with is about to be destructed. 143 // So, go ahead an unregister self from the ActiveDOMObject list, and 144 // set m_scriptExecutionContext to 0 so that ~ActiveDOMObject() doesn't 145 // try to do so again. 146 m_scriptExecutionContext->willDestroyActiveDOMObject(this); 147 m_scriptExecutionContext = 0; 139 ActiveDOMObject::contextDestroyed(); 148 140 } 149 141 … … 167 159 #if PLATFORM(IOS) 168 160 MutexLocker lock(m_databaseThreadMutex); 169 #endif //PLATFORM(IOS)161 #endif 170 162 // It's OK to ask for the m_databaseThread after we've requested 171 163 // termination because we're still using it to execute the closing … … 178 170 m_databaseThread = DatabaseThread::create(); 179 171 if (!m_databaseThread->start()) 180 m_databaseThread = 0; 172 m_databaseThread = nullptr; 173 181 174 #if PLATFORM(IOS) 182 175 if (m_databaseThread) 183 176 m_databaseThread->setPaused(m_paused); 184 #endif //PLATFORM(IOS)177 #endif 185 178 } 186 179 … … 199 192 #endif // PLATFORM(IOS) 200 193 201 bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync)194 bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* synchronizer) 202 195 { 203 196 if (m_isRegistered) { … … 217 210 218 211 if (m_databaseThread && !m_hasRequestedTermination) { 219 m_databaseThread->requestTermination( cleanupSync);212 m_databaseThread->requestTermination(synchronizer); 220 213 m_hasRequestedTermination = true; 221 214 return true; -
trunk/Source/WebCore/Modules/webdatabase/DatabaseContext.h
r165676 r167579 32 32 33 33 #include "ActiveDOMObject.h" 34 #include "DatabaseDetails.h" 35 #include <wtf/Assertions.h> 34 #include <wtf/RefPtr.h> 36 35 #include <wtf/ThreadSafeRefCounted.h> 37 36 38 37 #if PLATFORM(IOS) 39 38 #include <wtf/Threading.h> 40 #endif // PLATFORM(IOS)39 #endif 41 40 42 41 namespace WebCore { 43 42 44 43 class Database; 44 class DatabaseDetails; 45 45 class DatabaseBackendContext; 46 46 class DatabaseTaskSynchronizer; 47 47 class DatabaseThread; 48 class ScriptExecutionContext;49 48 50 class DatabaseContext : public ThreadSafeRefCounted<DatabaseContext>, ActiveDOMObject {49 class DatabaseContext : public ThreadSafeRefCounted<DatabaseContext>, private ActiveDOMObject { 51 50 public: 52 51 virtual ~DatabaseContext(); 53 52 54 // For life-cycle management (inherited from ActiveDOMObject):55 virtual void contextDestroyed();56 virtual void stop();57 58 53 PassRefPtr<DatabaseBackendContext> backend(); 59 54 DatabaseThread* databaseThread(); 55 60 56 #if PLATFORM(IOS) 61 57 void setPaused(bool); 62 #endif // PLATFORM(IOS)58 #endif 63 59 64 60 void setHasOpenDatabases() { m_hasOpenDatabases = true; } 65 61 bool hasOpenDatabases() { return m_hasOpenDatabases; } 66 62 67 // When the database cleanup is done, cleanupSyncwill be signalled.63 // When the database cleanup is done, the sychronizer will be signalled. 68 64 bool stopDatabases(DatabaseTaskSynchronizer*); 69 65 … … 74 70 explicit DatabaseContext(ScriptExecutionContext*); 75 71 76 void stopDatabases() { stopDatabases(0); } 72 void stopDatabases() { stopDatabases(nullptr); } 73 74 virtual void contextDestroyed() override final; 75 virtual void stop() override final; 77 76 78 77 RefPtr<DatabaseThread> m_databaseThread; … … 87 86 Mutex m_databaseThreadMutex; 88 87 bool m_paused; 89 #endif // PLATFORM(IOS)88 #endif 90 89 }; 91 90 -
trunk/Source/WebCore/dom/ActiveDOMObject.cpp
r165676 r167579 38 38 , m_pendingActivityCount(0) 39 39 #if !ASSERT_DISABLED 40 , m_suspendIfNeeded Called(false)40 , m_suspendIfNeededWasCalled(false) 41 41 #endif 42 42 { … … 45 45 46 46 ASSERT(m_scriptExecutionContext->isContextThread()); 47 m_scriptExecutionContext->didCreateActiveDOMObject( this);47 m_scriptExecutionContext->didCreateActiveDOMObject(*this); 48 48 } 49 49 … … 53 53 return; 54 54 55 ASSERT(m_suspendIfNeeded Called);55 ASSERT(m_suspendIfNeededWasCalled); 56 56 57 57 // ActiveDOMObject may be inherited by a sub-class whose life-cycle … … 63 63 if (m_scriptExecutionContext) { 64 64 ASSERT(m_scriptExecutionContext->isContextThread()); 65 m_scriptExecutionContext->willDestroyActiveDOMObject( this);65 m_scriptExecutionContext->willDestroyActiveDOMObject(*this); 66 66 } 67 67 } … … 70 70 { 71 71 #if !ASSERT_DISABLED 72 ASSERT(!m_suspendIfNeeded Called);73 m_suspendIfNeeded Called = true;72 ASSERT(!m_suspendIfNeededWasCalled); 73 m_suspendIfNeededWasCalled = true; 74 74 #endif 75 75 if (!m_scriptExecutionContext) 76 76 return; 77 77 78 m_scriptExecutionContext->suspendActiveDOMObjectIfNeeded( this);78 m_scriptExecutionContext->suspendActiveDOMObjectIfNeeded(*this); 79 79 } 80 81 #if !ASSERT_DISABLED 82 83 void ActiveDOMObject::assertSuspendIfNeededWasCalled() const 84 { 85 ASSERT(m_suspendIfNeededWasCalled); 86 } 87 88 #endif 80 89 81 90 bool ActiveDOMObject::hasPendingActivity() const -
trunk/Source/WebCore/dom/ActiveDOMObject.h
r165676 r167579 38 38 explicit ActiveDOMObject(ScriptExecutionContext*); 39 39 40 // suspendIfNeeded() should be called exactly once after object construction to synchronize41 // the suspend state with that inScriptExecutionContext.40 // The suspendIfNeeded must be called exactly once after object construction to update 41 // the suspended state to match that of the ScriptExecutionContext. 42 42 void suspendIfNeeded(); 43 #if !ASSERT_DISABLED 44 bool suspendIfNeededCalled() const { return m_suspendIfNeededCalled; } 45 #endif 43 void assertSuspendIfNeededWasCalled() const; 46 44 47 45 virtual bool hasPendingActivity() const; 48 46 49 // canSuspend() is used by the caller if there is a choice between suspending and stopping. 50 // For example, a page won't be suspended and placed in the back/forward cache if it has 51 // the objects that can not be suspended. 52 // However, 'suspend' can be called even if canSuspend() would return 'false'. That 53 // happens in step-by-step JS debugging for example - in this case it would be incorrect 54 // to stop the object. Exact semantics of suspend is up to the object then. 47 // The canSuspend function is used by the caller if there is a choice between suspending 48 // and stopping. For example, a page won't be suspended and placed in the back/forward 49 // cache if it contains any objects that cannot be suspended. 50 51 // However, the suspend function will sometimes be called even if canSuspend returns false. 52 // That happens in step-by-step JS debugging for example - in this case it would be incorrect 53 // to stop the object. Exact semantics of suspend is up to the object in cases like that. 54 55 55 enum ReasonForSuspension { 56 56 JavaScriptDebuggerPaused, … … 60 60 DocumentWillBePaused 61 61 }; 62 63 // These three functions must not have a side effect of creating or destroying 64 // any ActiveDOMObject. That means they must not result in calls to arbitrary JavaScript. 62 65 virtual bool canSuspend() const; 63 66 virtual void suspend(ReasonForSuspension); 64 67 virtual void resume(); 68 69 // This function must not have a side effect of creating an ActiveDOMObject. 70 // That means it must not result in calls to arbitrary JavaScript. 71 // It can, however, have a side effect of deleting an ActiveDOMObject. 65 72 virtual void stop(); 66 73 … … 69 76 ASSERT(thisObject == this); 70 77 thisObject->ref(); 71 m_pendingActivityCount++;78 ++m_pendingActivityCount; 72 79 } 73 80 … … 85 92 unsigned m_pendingActivityCount; 86 93 #if !ASSERT_DISABLED 87 bool m_suspendIfNeeded Called;94 bool m_suspendIfNeededWasCalled; 88 95 #endif 89 96 }; 97 98 #if ASSERT_DISABLED 99 100 inline void ActiveDOMObject::assertSuspendIfNeededWasCalled() const 101 { 102 } 103 104 #endif 90 105 91 106 } // namespace WebCore -
trunk/Source/WebCore/dom/ContextDestructionObserver.cpp
r165676 r167579 33 33 34 34 ContextDestructionObserver::ContextDestructionObserver(ScriptExecutionContext* scriptExecutionContext) 35 : m_scriptExecutionContext( 0)35 : m_scriptExecutionContext(nullptr) 36 36 { 37 37 observeContext(scriptExecutionContext); … … 40 40 ContextDestructionObserver::~ContextDestructionObserver() 41 41 { 42 observeContext( 0);42 observeContext(nullptr); 43 43 } 44 44 … … 47 47 if (m_scriptExecutionContext) { 48 48 ASSERT(m_scriptExecutionContext->isContextThread()); 49 m_scriptExecutionContext->willDestroyDestructionObserver( this);49 m_scriptExecutionContext->willDestroyDestructionObserver(*this); 50 50 } 51 51 … … 54 54 if (m_scriptExecutionContext) { 55 55 ASSERT(m_scriptExecutionContext->isContextThread()); 56 m_scriptExecutionContext->didCreateDestructionObserver( this);56 m_scriptExecutionContext->didCreateDestructionObserver(*this); 57 57 } 58 58 } … … 60 60 void ContextDestructionObserver::contextDestroyed() 61 61 { 62 m_scriptExecutionContext = 0;62 m_scriptExecutionContext = nullptr; 63 63 } 64 64 -
trunk/Source/WebCore/dom/MessagePort.cpp
r165676 r167579 41 41 , m_scriptExecutionContext(&scriptExecutionContext) 42 42 { 43 m_scriptExecutionContext->createdMessagePort( this);43 m_scriptExecutionContext->createdMessagePort(*this); 44 44 45 45 // Don't need to call processMessagePortMessagesSoon() here, because the port will not be opened until start() is invoked. … … 50 50 close(); 51 51 if (m_scriptExecutionContext) 52 m_scriptExecutionContext->destroyedMessagePort( this);52 m_scriptExecutionContext->destroyedMessagePort(*this); 53 53 } 54 54 … … 90 90 m_entangledChannel->disentangle(); 91 91 92 // We can't receive any messages or generate any events , so remove ourselves from the list of active ports.93 ASSERT(m_scriptExecutionContext); 94 m_scriptExecutionContext->destroyedMessagePort( this);95 m_scriptExecutionContext = 0;92 // We can't receive any messages or generate any events after this, so remove ourselves from the list of active ports. 93 ASSERT(m_scriptExecutionContext); 94 m_scriptExecutionContext->destroyedMessagePort(*this); 95 m_scriptExecutionContext = nullptr; 96 96 97 97 return std::move(m_entangledChannel); -
trunk/Source/WebCore/dom/ScriptExecutionContext.cpp
r165710 r167579 93 93 94 94 ScriptExecutionContext::ScriptExecutionContext() 95 : m_iteratingActiveDOMObjects(false) 96 , m_inDestructor(false) 97 , m_circularSequentialID(0) 95 : m_circularSequentialID(0) 98 96 , m_inDispatchErrorEvent(false) 99 97 , m_activeDOMObjectsAreSuspended(false) 100 98 , m_reasonForSuspendingActiveDOMObjects(static_cast<ActiveDOMObject::ReasonForSuspension>(-1)) 101 99 , m_activeDOMObjectsAreStopped(false) 102 { 103 } 100 , m_activeDOMObjectAdditionForbidden(false) 101 #if !ASSERT_DISABLED 102 , m_inScriptExecutionContextDestructor(false) 103 , m_activeDOMObjectRemovalForbidden(false) 104 #endif 105 { 106 } 107 108 // FIXME: We should make this a member function of HashSet. 109 template<typename T> inline T takeAny(HashSet<T>& set) 110 { 111 ASSERT(!set.isEmpty()); 112 auto iterator = set.begin(); 113 T result = std::move(*iterator); 114 set.remove(iterator); 115 return result; 116 } 117 118 #if ASSERT_DISABLED 119 120 inline void ScriptExecutionContext::checkConsistency() const 121 { 122 } 123 124 #else 125 126 void ScriptExecutionContext::checkConsistency() const 127 { 128 for (auto* messagePort : m_messagePorts) 129 ASSERT(messagePort->scriptExecutionContext() == this); 130 131 for (auto* destructionObserver : m_destructionObservers) 132 ASSERT(destructionObserver->scriptExecutionContext() == this); 133 134 for (auto* activeDOMObject : m_activeDOMObjects) { 135 ASSERT(activeDOMObject->scriptExecutionContext() == this); 136 activeDOMObject->assertSuspendIfNeededWasCalled(); 137 } 138 } 139 140 #endif 104 141 105 142 ScriptExecutionContext::~ScriptExecutionContext() 106 143 { 107 m_inDestructor = true; 108 for (HashSet<ContextDestructionObserver*>::iterator iter = m_destructionObservers.begin(); iter != m_destructionObservers.end(); iter = m_destructionObservers.begin()) { 109 ContextDestructionObserver* observer = *iter; 110 m_destructionObservers.remove(observer); 111 ASSERT(observer->scriptExecutionContext() == this); 112 observer->contextDestroyed(); 113 } 114 115 HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end(); 116 for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { 117 ASSERT((*iter)->scriptExecutionContext() == this); 118 (*iter)->contextDestroyed(); 119 } 144 checkConsistency(); 145 146 #if !ASSERT_DISABLED 147 m_inScriptExecutionContextDestructor = true; 148 #endif 149 150 while (!m_destructionObservers.isEmpty()) 151 takeAny(m_destructionObservers)->contextDestroyed(); 152 153 for (auto* messagePort : m_messagePorts) 154 messagePort->contextDestroyed(); 155 156 #if !ASSERT_DISABLED 157 m_inScriptExecutionContextDestructor = false; 158 #endif 120 159 } 121 160 … … 127 166 void ScriptExecutionContext::dispatchMessagePortEvents() 128 167 { 168 checkConsistency(); 169 129 170 Ref<ScriptExecutionContext> protect(*this); 130 171 131 // Make a frozen copy. 132 Vector<MessagePort*> ports; 133 copyToVector(m_messagePorts, ports); 134 135 unsigned portCount = ports.size(); 136 for (unsigned i = 0; i < portCount; ++i) { 137 MessagePort* port = ports[i]; 138 // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen 139 // as a result is that dispatchMessages() will be called needlessly. 140 if (m_messagePorts.contains(port) && port->started()) 141 port->dispatchMessages(); 142 } 143 } 144 145 void ScriptExecutionContext::createdMessagePort(MessagePort* port) 146 { 147 ASSERT(port); 172 // Make a frozen copy of the ports so we can iterate while new ones might be added or destroyed. 173 Vector<MessagePort*> possibleMessagePorts; 174 copyToVector(m_messagePorts, possibleMessagePorts); 175 for (auto* messagePort : possibleMessagePorts) { 176 // The port may be destroyed, and another one created at the same address, 177 // but this is harmless. The worst that can happen as a result is that 178 // dispatchMessages() will be called needlessly. 179 if (m_messagePorts.contains(messagePort) && messagePort->started()) 180 messagePort->dispatchMessages(); 181 } 182 } 183 184 void ScriptExecutionContext::createdMessagePort(MessagePort& messagePort) 185 { 148 186 ASSERT((isDocument() && isMainThread()) 149 187 || (isWorkerGlobalScope() && currentThread() == toWorkerGlobalScope(this)->thread().threadID())); 150 188 151 m_messagePorts.add(port); 152 } 153 154 void ScriptExecutionContext::destroyedMessagePort(MessagePort* port) 155 { 156 ASSERT(port); 189 m_messagePorts.add(&messagePort); 190 } 191 192 void ScriptExecutionContext::destroyedMessagePort(MessagePort& messagePort) 193 { 157 194 ASSERT((isDocument() && isMainThread()) 158 195 || (isWorkerGlobalScope() && currentThread() == toWorkerGlobalScope(this)->thread().threadID())); 159 196 160 m_messagePorts.remove( port);197 m_messagePorts.remove(&messagePort); 161 198 } 162 199 163 200 bool ScriptExecutionContext::canSuspendActiveDOMObjects() 164 201 { 165 // No protection against m_activeDOMObjects changing during iteration: canSuspend() shouldn't execute arbitrary JS. 166 m_iteratingActiveDOMObjects = true; 167 ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end(); 168 for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { 169 ASSERT((*iter)->scriptExecutionContext() == this); 170 ASSERT((*iter)->suspendIfNeededCalled()); 171 if (!(*iter)->canSuspend()) { 172 m_iteratingActiveDOMObjects = false; 173 return false; 202 checkConsistency(); 203 204 bool canSuspend = true; 205 206 m_activeDOMObjectAdditionForbidden = true; 207 #if !ASSERT_DISABLED 208 m_activeDOMObjectRemovalForbidden = true; 209 #endif 210 211 // We assume that m_activeDOMObjects will not change during iteration: canSuspend 212 // functions should not add new active DOM objects, nor execute arbitrary JavaScript. 213 // An ASSERT or RELEASE_ASSERT will fire if this happens, but it's important to code 214 // canSuspend functions so it will not happen! 215 for (auto* activeDOMObject : m_activeDOMObjects) { 216 if (!activeDOMObject->canSuspend()) { 217 canSuspend = false; 218 break; 174 219 } 175 220 } 176 m_iteratingActiveDOMObjects = false; 177 return true; 221 222 m_activeDOMObjectAdditionForbidden = false; 223 #if !ASSERT_DISABLED 224 m_activeDOMObjectRemovalForbidden = false; 225 #endif 226 227 return canSuspend; 178 228 } 179 229 180 230 void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why) 181 231 { 232 checkConsistency(); 233 182 234 #if PLATFORM(IOS) 183 235 if (m_activeDOMObjectsAreSuspended) { … … 187 239 #endif 188 240 189 // No protection against m_activeDOMObjects changing during iteration: suspend() shouldn't execute arbitrary JS. 190 m_iteratingActiveDOMObjects = true; 191 ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end(); 192 for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { 193 ASSERT((*iter)->scriptExecutionContext() == this); 194 ASSERT((*iter)->suspendIfNeededCalled()); 195 (*iter)->suspend(why); 196 } 197 m_iteratingActiveDOMObjects = false; 241 m_activeDOMObjectAdditionForbidden = true; 242 #if !ASSERT_DISABLED 243 m_activeDOMObjectRemovalForbidden = true; 244 #endif 245 246 // We assume that m_activeDOMObjects will not change during iteration: suspend 247 // functions should not add new active DOM objects, nor execute arbitrary JavaScript. 248 // An ASSERT or RELEASE_ASSERT will fire if this happens, but it's important to code 249 // suspend functions so it will not happen! 250 for (auto* activeDOMObject : m_activeDOMObjects) 251 activeDOMObject->suspend(why); 252 253 m_activeDOMObjectAdditionForbidden = false; 254 #if !ASSERT_DISABLED 255 m_activeDOMObjectRemovalForbidden = false; 256 #endif 257 198 258 m_activeDOMObjectsAreSuspended = true; 199 259 m_reasonForSuspendingActiveDOMObjects = why; … … 202 262 void ScriptExecutionContext::resumeActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why) 203 263 { 264 checkConsistency(); 265 204 266 if (m_reasonForSuspendingActiveDOMObjects != why) 205 267 return; 206 207 268 m_activeDOMObjectsAreSuspended = false; 208 // No protection against m_activeDOMObjects changing during iteration: resume() shouldn't execute arbitrary JS. 209 m_iteratingActiveDOMObjects = true; 210 ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end(); 211 for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { 212 ASSERT((*iter)->scriptExecutionContext() == this); 213 ASSERT((*iter)->suspendIfNeededCalled()); 214 (*iter)->resume(); 215 } 216 m_iteratingActiveDOMObjects = false; 269 270 m_activeDOMObjectAdditionForbidden = true; 271 #if !ASSERT_DISABLED 272 m_activeDOMObjectRemovalForbidden = true; 273 #endif 274 275 // We assume that m_activeDOMObjects will not change during iteration: resume 276 // functions should not add new active DOM objects, nor execute arbitrary JavaScript. 277 // An ASSERT or RELEASE_ASSERT will fire if this happens, but it's important to code 278 // resume functions so it will not happen! 279 for (auto* activeDOMObject : m_activeDOMObjects) 280 activeDOMObject->resume(); 281 282 m_activeDOMObjectAdditionForbidden = false; 283 #if !ASSERT_DISABLED 284 m_activeDOMObjectRemovalForbidden = false; 285 #endif 217 286 } 218 287 219 288 void ScriptExecutionContext::stopActiveDOMObjects() 220 289 { 290 checkConsistency(); 291 221 292 if (m_activeDOMObjectsAreStopped) 222 293 return; 223 294 m_activeDOMObjectsAreStopped = true; 224 // No protection against m_activeDOMObjects changing during iteration: stop() shouldn't execute arbitrary JS. 225 m_iteratingActiveDOMObjects = true; 226 ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end(); 227 for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { 228 ASSERT((*iter)->scriptExecutionContext() == this); 229 ASSERT((*iter)->suspendIfNeededCalled()); 230 (*iter)->stop(); 231 } 232 m_iteratingActiveDOMObjects = false; 233 234 // Also close MessagePorts. If they were ActiveDOMObjects (they could be) then they could be stopped instead. 235 closeMessagePorts(); 236 } 237 238 void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject* object) 239 { 240 ASSERT(m_activeDOMObjects.contains(object)); 241 // Ensure all ActiveDOMObjects are suspended also newly created ones. 295 296 // Make a frozen copy of the objects so we can iterate while new ones might be destroyed. 297 Vector<ActiveDOMObject*> possibleActiveDOMObjects; 298 copyToVector(m_activeDOMObjects, possibleActiveDOMObjects); 299 300 m_activeDOMObjectAdditionForbidden = true; 301 302 // We assume that new objects will not be added to m_activeDOMObjects during iteration: 303 // stop functions should not add new active DOM objects, nor execute arbitrary JavaScript. 304 // A RELEASE_ASSERT will fire if this happens, but it's important to code stop functions 305 // so it will not happen! 306 for (auto* activeDOMObject : possibleActiveDOMObjects) { 307 // Check if this object was deleted already. If so, just skip it. 308 // Calling contains on a possibly-already-deleted object is OK because we guarantee 309 // no new object can be added, so even if a new object ends up allocated with the 310 // same address, that will be *after* this function exits. 311 if (!m_activeDOMObjects.contains(activeDOMObject)) 312 continue; 313 activeDOMObject->stop(); 314 } 315 316 m_activeDOMObjectAdditionForbidden = false; 317 318 // FIXME: Make message ports be active DOM objects and let them implement stop instead 319 // of having this separate mechanism just for them. 320 for (auto* messagePort : m_messagePorts) 321 messagePort->close(); 322 } 323 324 void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject& activeDOMObject) 325 { 326 ASSERT(m_activeDOMObjects.contains(&activeDOMObject)); 242 327 if (m_activeDOMObjectsAreSuspended) 243 object->suspend(m_reasonForSuspendingActiveDOMObjects);328 activeDOMObject.suspend(m_reasonForSuspendingActiveDOMObjects); 244 329 if (m_activeDOMObjectsAreStopped) 245 object->stop(); 246 } 247 248 void ScriptExecutionContext::didCreateActiveDOMObject(ActiveDOMObject* object) 249 { 250 ASSERT(object); 251 ASSERT(!m_inDestructor); 252 if (m_iteratingActiveDOMObjects) 253 CRASH(); 254 m_activeDOMObjects.add(object); 255 } 256 257 void ScriptExecutionContext::willDestroyActiveDOMObject(ActiveDOMObject* object) 258 { 259 ASSERT(object); 260 if (m_iteratingActiveDOMObjects) 261 CRASH(); 262 m_activeDOMObjects.remove(object); 263 } 264 265 void ScriptExecutionContext::didCreateDestructionObserver(ContextDestructionObserver* observer) 266 { 267 ASSERT(observer); 268 ASSERT(!m_inDestructor); 269 m_destructionObservers.add(observer); 270 } 271 272 void ScriptExecutionContext::willDestroyDestructionObserver(ContextDestructionObserver* observer) 273 { 274 ASSERT(observer); 275 m_destructionObservers.remove(observer); 276 } 277 278 void ScriptExecutionContext::closeMessagePorts() { 279 HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end(); 280 for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { 281 ASSERT((*iter)->scriptExecutionContext() == this); 282 (*iter)->close(); 283 } 330 activeDOMObject.stop(); 331 } 332 333 void ScriptExecutionContext::didCreateActiveDOMObject(ActiveDOMObject& activeDOMObject) 334 { 335 // The m_activeDOMObjectAdditionForbidden check is a RELEASE_ASSERT because of the 336 // consequences of having an ActiveDOMObject that is not correctly reflected in the set. 337 // If we do have one of those, it can possibly be a security vulnerability. So we'd 338 // rather have a crash than continue running with the set possibly compromised. 339 ASSERT(!m_inScriptExecutionContextDestructor); 340 RELEASE_ASSERT(!m_activeDOMObjectAdditionForbidden); 341 m_activeDOMObjects.add(&activeDOMObject); 342 } 343 344 void ScriptExecutionContext::willDestroyActiveDOMObject(ActiveDOMObject& activeDOMObject) 345 { 346 ASSERT(!m_activeDOMObjectRemovalForbidden); 347 m_activeDOMObjects.remove(&activeDOMObject); 348 } 349 350 void ScriptExecutionContext::didCreateDestructionObserver(ContextDestructionObserver& observer) 351 { 352 ASSERT(!m_inScriptExecutionContextDestructor); 353 m_destructionObservers.add(&observer); 354 } 355 356 void ScriptExecutionContext::willDestroyDestructionObserver(ContextDestructionObserver& observer) 357 { 358 m_destructionObservers.remove(&observer); 284 359 } 285 360 … … 370 445 { 371 446 if (minimumTimerInterval() != oldMinimumTimerInterval) { 372 for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) { 373 DOMTimer* timer = iter->value; 447 for (auto* timer : m_timeouts.values()) 374 448 timer->adjustMinimumTimerInterval(oldMinimumTimerInterval); 375 }376 449 } 377 450 } … … 389 462 void ScriptExecutionContext::didChangeTimerAlignmentInterval() 390 463 { 391 for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) { 392 DOMTimer* timer = iter->value; 464 for (auto* timer : m_timeouts.values()) 393 465 timer->didChangeAlignmentInterval(); 394 }395 466 } 396 467 … … 420 491 #endif 421 492 493 bool ScriptExecutionContext::hasPendingActivity() const 494 { 495 checkConsistency(); 496 497 for (auto* activeDOMObject : m_activeDOMObjects) { 498 if (activeDOMObject->hasPendingActivity()) 499 return true; 500 } 501 502 for (auto* messagePort : m_messagePorts) { 503 if (messagePort->hasPendingActivity()) 504 return true; 505 } 506 507 return false; 508 } 509 422 510 } // namespace WebCore -
trunk/Source/WebCore/dom/ScriptExecutionContext.h
r165710 r167579 32 32 #include "SecurityContext.h" 33 33 #include "Supplementable.h" 34 #include "URL.h"35 34 #include <runtime/ConsoleTypes.h> 36 35 #include <wtf/HashSet.h> … … 50 49 class DatabaseContext; 51 50 class DOMTimer; 52 class EventListener;53 51 class EventQueue; 54 52 class EventTarget; 55 53 class MessagePort; 56 57 #if ENABLE(BLOB)58 54 class PublicURLManager; 59 #endif 55 class URL; 60 56 61 57 class ScriptExecutionContext : public SecurityContext, public Supplementable<ScriptExecutionContext> { … … 77 73 virtual void disableEval(const String& errorMessage) = 0; 78 74 79 bool sanitizeScriptError(String& errorMessage, int& lineNumber, int& columnNumber, String& sourceURL, CachedScript* = 0); 80 // FIXME: <http://webkit.org/b/114315> ScriptExecutionContext log exception should include a column number 81 void reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<Inspector::ScriptCallStack>, CachedScript* = 0); 82 83 void addConsoleMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, JSC::ExecState* = 0, unsigned long requestIdentifier = 0); 75 bool sanitizeScriptError(String& errorMessage, int& lineNumber, int& columnNumber, String& sourceURL, CachedScript* = nullptr); 76 void reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<Inspector::ScriptCallStack>, CachedScript* = nullptr); 77 78 void addConsoleMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, JSC::ExecState* = nullptr, unsigned long requestIdentifier = 0); 84 79 virtual void addConsoleMessage(MessageSource, MessageLevel, const String& message, unsigned long requestIdentifier = 0) = 0; 85 80 … … 102 97 103 98 // Called from the constructor and destructors of ActiveDOMObject. 104 void didCreateActiveDOMObject(ActiveDOMObject *);105 void willDestroyActiveDOMObject(ActiveDOMObject *);99 void didCreateActiveDOMObject(ActiveDOMObject&); 100 void willDestroyActiveDOMObject(ActiveDOMObject&); 106 101 107 102 // Called after the construction of an ActiveDOMObject to synchronize suspend state. 108 void suspendActiveDOMObjectIfNeeded(ActiveDOMObject*); 109 110 typedef HashSet<ActiveDOMObject*> ActiveDOMObjectsSet; 111 const ActiveDOMObjectsSet& activeDOMObjects() const { return m_activeDOMObjects; } 112 113 void didCreateDestructionObserver(ContextDestructionObserver*); 114 void willDestroyDestructionObserver(ContextDestructionObserver*); 103 void suspendActiveDOMObjectIfNeeded(ActiveDOMObject&); 104 105 void didCreateDestructionObserver(ContextDestructionObserver&); 106 void willDestroyDestructionObserver(ContextDestructionObserver&); 115 107 116 108 // MessagePort is conceptually a kind of ActiveDOMObject, but it needs to be tracked separately for message dispatch. 117 109 void processMessagePortMessagesSoon(); 118 110 void dispatchMessagePortEvents(); 119 void createdMessagePort(MessagePort*); 120 void destroyedMessagePort(MessagePort*); 121 const HashSet<MessagePort*>& messagePorts() const { return m_messagePorts; } 111 void createdMessagePort(MessagePort&); 112 void destroyedMessagePort(MessagePort&); 122 113 123 114 void ref() { refScriptExecutionContext(); } … … 186 177 ActiveDOMObject::ReasonForSuspension reasonForSuspendingActiveDOMObjects() const { return m_reasonForSuspendingActiveDOMObjects; } 187 178 179 bool hasPendingActivity() const; 180 188 181 private: 189 virtual void addMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, PassRefPtr<Inspector::ScriptCallStack>, JSC::ExecState* = 0, unsigned long requestIdentifier = 0) = 0;182 virtual void addMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, PassRefPtr<Inspector::ScriptCallStack>, JSC::ExecState* = nullptr, unsigned long requestIdentifier = 0) = 0; 190 183 virtual EventTarget* errorEventTarget() = 0; 191 184 virtual void logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtr<Inspector::ScriptCallStack>) = 0; 192 185 bool dispatchErrorEvent(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, CachedScript*); 193 186 194 void closeMessagePorts();195 196 187 virtual void refScriptExecutionContext() = 0; 197 188 virtual void derefScriptExecutionContext() = 0; 198 189 190 void checkConsistency() const; 191 199 192 HashSet<MessagePort*> m_messagePorts; 200 193 HashSet<ContextDestructionObserver*> m_destructionObservers; 201 ActiveDOMObjectsSet m_activeDOMObjects; 202 bool m_iteratingActiveDOMObjects; 203 bool m_inDestructor; 194 HashSet<ActiveDOMObject*> m_activeDOMObjects; 204 195 205 196 int m_circularSequentialID; 206 typedef HashMap<int, DOMTimer*> TimeoutMap; 207 TimeoutMap m_timeouts; 197 HashMap<int, DOMTimer*> m_timeouts; 208 198 209 199 bool m_inDispatchErrorEvent; … … 222 212 RefPtr<DatabaseContext> m_databaseContext; 223 213 #endif 214 215 bool m_activeDOMObjectAdditionForbidden; 216 217 #if !ASSERT_DISABLED 218 bool m_inScriptExecutionContextDestructor; 219 bool m_activeDOMObjectRemovalForbidden; 220 #endif 224 221 }; 225 222 -
trunk/Source/WebCore/workers/WorkerGlobalScope.cpp
r165676 r167579 160 160 } 161 161 162 bool WorkerGlobalScope::hasPendingActivity() const163 {164 ActiveDOMObjectsSet::const_iterator activeObjectsEnd = activeDOMObjects().end();165 for (ActiveDOMObjectsSet::const_iterator iter = activeDOMObjects().begin(); iter != activeObjectsEnd; ++iter) {166 if ((*iter)->hasPendingActivity())167 return true;168 }169 170 HashSet<MessagePort*>::const_iterator messagePortsEnd = messagePorts().end();171 for (HashSet<MessagePort*>::const_iterator iter = messagePorts().begin(); iter != messagePortsEnd; ++iter) {172 if ((*iter)->hasPendingActivity())173 return true;174 }175 176 return false;177 }178 179 162 void WorkerGlobalScope::postTask(PassOwnPtr<Task> task) 180 163 { -
trunk/Source/WebCore/workers/WorkerGlobalScope.h
r165676 r167579 77 77 WorkerThread& thread() const { return m_thread; } 78 78 79 bool hasPendingActivity() const;79 using ScriptExecutionContext::hasPendingActivity; 80 80 81 81 virtual void postTask(PassOwnPtr<Task>) override; // Executes the task on context's thread asynchronously.
Note: See TracChangeset
for help on using the changeset viewer.