1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set sw=2 ts=8 et tw=80 :
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "ChannelEventQueue.h"
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Unused.h"
12 #include "nsIChannel.h"
13 #include "mozilla/dom/Document.h"
14 #include "nsThreadUtils.h"
19 ChannelEvent
* ChannelEventQueue::TakeEvent() {
20 mMutex
.AssertCurrentThreadOwns();
21 MOZ_ASSERT(mFlushing
);
23 if (mSuspended
|| mEventQueue
.IsEmpty()) {
27 UniquePtr
<ChannelEvent
> event(std::move(mEventQueue
[0]));
28 mEventQueue
.RemoveElementAt(0);
30 return event
.release();
33 void ChannelEventQueue::FlushQueue() {
34 mMutex
.AssertCurrentThreadOwns();
35 // Events flushed could include destruction of channel (and our own
36 // destructor) unless we make sure its refcount doesn't drop to 0 while this
38 nsCOMPtr
<nsISupports
> kungFuDeathGrip
;
39 kungFuDeathGrip
= mOwner
;
40 mozilla::Unused
<< kungFuDeathGrip
; // Not used in this function
42 MOZ_ASSERT(mFlushing
);
44 bool needResumeOnOtherThread
= false;
47 UniquePtr
<ChannelEvent
> event
;
48 event
.reset(TakeEvent());
50 MOZ_ASSERT(mFlushing
);
52 MOZ_ASSERT(mEventQueue
.IsEmpty() || (mSuspended
|| !!mForcedCount
));
56 nsCOMPtr
<nsIEventTarget
> target
= event
->GetEventTarget();
59 bool isCurrentThread
= false;
60 nsresult rv
= target
->IsOnCurrentThread(&isCurrentThread
);
61 if (NS_WARN_IF(NS_FAILED(rv
))) {
62 // Simply run this event on current thread if we are not sure about it
63 // in release channel, or assert in Aurora/Nightly channel.
64 MOZ_DIAGNOSTIC_CRASH("IsOnCurrentThread failed");
65 isCurrentThread
= true;
68 if (!isCurrentThread
) {
69 // Next event needs to run on another thread. Put it back to
70 // the front of the queue can try resume on that thread.
72 PrependEventInternal(std::move(event
));
74 needResumeOnOtherThread
= true;
75 MOZ_ASSERT(mFlushing
);
77 MOZ_ASSERT(!mEventQueue
.IsEmpty());
81 MutexAutoUnlock
unlock(mMutex
);
84 } // end of while(true)
86 // The flush procedure is aborted because next event cannot be run on current
87 // thread. We need to resume the event processing right after flush procedure
89 // Note: we cannot call Resume() while "mFlushing == true" because
90 // CompleteResume will not trigger FlushQueue while there is an ongoing flush.
91 if (needResumeOnOtherThread
) {
96 void ChannelEventQueue::Suspend() {
97 MutexAutoLock
lock(mMutex
);
101 void ChannelEventQueue::SuspendInternal() {
102 mMutex
.AssertCurrentThreadOwns();
108 void ChannelEventQueue::Resume() {
109 MutexAutoLock
lock(mMutex
);
113 void ChannelEventQueue::ResumeInternal() {
114 mMutex
.AssertCurrentThreadOwns();
116 // Resuming w/o suspend: error in debug mode, ignore in build
117 MOZ_ASSERT(mSuspendCount
> 0);
118 if (mSuspendCount
<= 0) {
122 if (!--mSuspendCount
) {
123 if (mEventQueue
.IsEmpty() || !!mForcedCount
) {
124 // Nothing in queue to flush or waiting for AutoEventEnqueuer to
125 // finish the force enqueue period, simply clear the flag.
130 // Hold a strong reference of mOwner to avoid the channel release
131 // before CompleteResume was executed.
132 class CompleteResumeRunnable
: public Runnable
{
134 explicit CompleteResumeRunnable(ChannelEventQueue
* aQueue
,
136 : Runnable("CompleteResumeRunnable"),
140 NS_IMETHOD
Run() override
{
141 mQueue
->CompleteResume();
146 virtual ~CompleteResumeRunnable() = default;
148 RefPtr
<ChannelEventQueue
> mQueue
;
149 nsCOMPtr
<nsISupports
> mOwner
;
156 // Worker thread requires a CancelableRunnable.
157 RefPtr
<Runnable
> event
= new CompleteResumeRunnable(this, mOwner
);
159 nsCOMPtr
<nsIEventTarget
> target
;
160 target
= mEventQueue
[0]->GetEventTarget();
163 Unused
<< NS_WARN_IF(
164 NS_FAILED(target
->Dispatch(event
.forget(), NS_DISPATCH_NORMAL
)));
168 bool ChannelEventQueue::MaybeSuspendIfEventsAreSuppressed() {
169 // We only ever need to suppress events on the main thread, since this is
170 // where content scripts can run.
171 if (!NS_IsMainThread()) {
175 // Only suppress events for queues associated with XHRs, as these can cause
176 // content scripts to run.
177 if (mHasCheckedForAsyncXMLHttpRequest
&& !mForAsyncXMLHttpRequest
) {
181 mMutex
.AssertCurrentThreadOwns();
182 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(mOwner
));
187 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
188 // Figure out if this is for an Async XHR, if we haven't done so already.
189 // We don't want to suspend Sync XHRs, as they'll always suspend event
190 // handling on the document, but we still need to process events for them.
191 if (!mHasCheckedForAsyncXMLHttpRequest
) {
192 nsContentPolicyType contentType
= loadInfo
->InternalContentPolicyType();
193 mForAsyncXMLHttpRequest
=
194 contentType
== nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC
;
195 mHasCheckedForAsyncXMLHttpRequest
= true;
197 if (!mForAsyncXMLHttpRequest
) {
202 // Suspend the queue if the associated document has suppressed event handling.
203 RefPtr
<dom::Document
> document
;
204 loadInfo
->GetLoadingDocument(getter_AddRefs(document
));
205 if (document
&& document
->EventHandlingSuppressed()) {
206 document
->AddSuspendedChannelEventQueue(this);
215 } // namespace mozilla