tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ChannelEventQueue.cpp (6401B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set sw=2 ts=8 et tw=80 :
      3 */
      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/. */
      7 
      8 #include "ChannelEventQueue.h"
      9 
     10 #include "mozilla/Assertions.h"
     11 #include "nsIChannel.h"
     12 #include "mozilla/dom/Document.h"
     13 #include "nsThreadUtils.h"
     14 #include "mozilla/FlowMarkers.h"
     15 
     16 namespace mozilla {
     17 namespace net {
     18 
     19 ChannelEvent* ChannelEventQueue::TakeEvent() {
     20  mMutex.AssertCurrentThreadOwns();
     21  MOZ_ASSERT(mFlushing);
     22 
     23  if (mSuspended || mEventQueue.IsEmpty()) {
     24    return nullptr;
     25  }
     26 
     27  UniquePtr<ChannelEvent> event(std::move(mEventQueue[0]));
     28  mEventQueue.RemoveElementAt(0);
     29 
     30  return event.release();
     31 }
     32 
     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
     37  // method is running.
     38  nsCOMPtr<nsISupports> kungFuDeathGrip;
     39  kungFuDeathGrip = mOwner;
     40  (void)kungFuDeathGrip;  // Not used in this function
     41 
     42  MOZ_ASSERT(mFlushing);
     43 
     44  bool needResumeOnOtherThread = false;
     45 
     46  while (true) {
     47    UniquePtr<ChannelEvent> event;
     48    event.reset(TakeEvent());
     49    if (!event) {
     50      MOZ_ASSERT(mFlushing);
     51      mFlushing = false;
     52      MOZ_ASSERT(mEventQueue.IsEmpty() || (mSuspended || !!mForcedCount));
     53      break;
     54    }
     55 
     56    nsCOMPtr<nsIEventTarget> target = event->GetEventTarget();
     57    MOZ_ASSERT(target);
     58 
     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;
     66    }
     67 
     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.
     71      SuspendInternal();
     72      PrependEventInternal(std::move(event));
     73 
     74      needResumeOnOtherThread = true;
     75      MOZ_ASSERT(mFlushing);
     76      mFlushing = false;
     77      MOZ_ASSERT(!mEventQueue.IsEmpty());
     78      break;
     79    }
     80    {
     81      MutexAutoUnlock unlock(mMutex);
     82      AUTO_PROFILER_TERMINATING_FLOW_MARKER("ChannelEvent", NETWORK,
     83                                            Flow::FromPointer(event.get()));
     84      event->Run();
     85    }
     86  }  // end of while(true)
     87 
     88  // The flush procedure is aborted because next event cannot be run on current
     89  // thread. We need to resume the event processing right after flush procedure
     90  // is finished.
     91  // Note: we cannot call Resume() while "mFlushing == true" because
     92  // CompleteResume will not trigger FlushQueue while there is an ongoing flush.
     93  if (needResumeOnOtherThread) {
     94    ResumeInternal();
     95  }
     96 }
     97 
     98 void ChannelEventQueue::Suspend() {
     99  MutexAutoLock lock(mMutex);
    100  SuspendInternal();
    101 }
    102 
    103 void ChannelEventQueue::SuspendInternal() {
    104  mMutex.AssertCurrentThreadOwns();
    105 
    106  mSuspended = true;
    107  mSuspendCount++;
    108 }
    109 
    110 void ChannelEventQueue::Resume() {
    111  MutexAutoLock lock(mMutex);
    112  ResumeInternal();
    113 }
    114 
    115 void ChannelEventQueue::ResumeInternal() {
    116  mMutex.AssertCurrentThreadOwns();
    117 
    118  // Resuming w/o suspend: error in debug mode, ignore in build
    119  MOZ_ASSERT(mSuspendCount > 0);
    120  if (mSuspendCount <= 0) {
    121    return;
    122  }
    123 
    124  if (!--mSuspendCount) {
    125    if (mEventQueue.IsEmpty() || !!mForcedCount) {
    126      // Nothing in queue to flush or waiting for AutoEventEnqueuer to
    127      // finish the force enqueue period, simply clear the flag.
    128      mSuspended = false;
    129      return;
    130    }
    131 
    132    // Hold a strong reference of mOwner to avoid the channel release
    133    // before CompleteResume was executed.
    134    class CompleteResumeRunnable : public Runnable {
    135     public:
    136      explicit CompleteResumeRunnable(ChannelEventQueue* aQueue,
    137                                      nsISupports* aOwner)
    138          : Runnable("CompleteResumeRunnable"),
    139            mQueue(aQueue),
    140            mOwner(aOwner) {}
    141 
    142      NS_IMETHOD Run() override {
    143        mQueue->CompleteResume();
    144        return NS_OK;
    145      }
    146 
    147     private:
    148      virtual ~CompleteResumeRunnable() = default;
    149 
    150      RefPtr<ChannelEventQueue> mQueue;
    151      nsCOMPtr<nsISupports> mOwner;
    152    };
    153 
    154    if (!mOwner) {
    155      return;
    156    }
    157 
    158    // Worker thread requires a CancelableRunnable.
    159    RefPtr<Runnable> event = new CompleteResumeRunnable(this, mOwner);
    160 
    161    nsCOMPtr<nsIEventTarget> target;
    162    target = mEventQueue[0]->GetEventTarget();
    163    MOZ_ASSERT(target);
    164 
    165    (void)NS_WARN_IF(
    166        NS_FAILED(target->Dispatch(event.forget(), NS_DISPATCH_NORMAL)));
    167  }
    168 }
    169 
    170 bool ChannelEventQueue::MaybeSuspendIfEventsAreSuppressed() {
    171  // We only ever need to suppress events on the main thread, since this is
    172  // where content scripts can run.
    173  if (!NS_IsMainThread()) {
    174    return false;
    175  }
    176 
    177  // Only suppress events for queues associated with XHRs, as these can cause
    178  // content scripts to run.
    179  if (mHasCheckedForAsyncXMLHttpRequest && !mForAsyncXMLHttpRequest) {
    180    return false;
    181  }
    182 
    183  mMutex.AssertCurrentThreadOwns();
    184  nsCOMPtr<nsIChannel> channel(do_QueryInterface(mOwner));
    185  if (!channel) {
    186    return false;
    187  }
    188 
    189  nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
    190  // Figure out if this is for an Async XHR, if we haven't done so already.
    191  // We don't want to suspend Sync XHRs, as they'll always suspend event
    192  // handling on the document, but we still need to process events for them.
    193  if (!mHasCheckedForAsyncXMLHttpRequest) {
    194    nsContentPolicyType contentType = loadInfo->InternalContentPolicyType();
    195    mForAsyncXMLHttpRequest =
    196        contentType == nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC;
    197    mHasCheckedForAsyncXMLHttpRequest = true;
    198 
    199    if (!mForAsyncXMLHttpRequest) {
    200      return false;
    201    }
    202  }
    203 
    204  // Suspend the queue if the associated document has suppressed event handling.
    205  RefPtr<dom::Document> document;
    206  loadInfo->GetLoadingDocument(getter_AddRefs(document));
    207  if (document && document->EventHandlingSuppressed()) {
    208    document->AddSuspendedChannelEventQueue(this);
    209    SuspendInternal();
    210    return true;
    211  }
    212 
    213  return false;
    214 }
    215 
    216 }  // namespace net
    217 }  // namespace mozilla