tor-browser

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

platform_uithread.cc (6288B)


      1 /*
      2 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #if defined(WEBRTC_WIN)
     12 
     13 #  include "platform_uithread.h"
     14 
     15 namespace webrtc {
     16 
     17 // timer id used in delayed callbacks
     18 static const UINT_PTR kTimerId = 1;
     19 static const wchar_t kThisProperty[] = L"ThreadWindowsUIPtr";
     20 static const wchar_t kThreadWindow[] = L"WebrtcWindowsUIThread";
     21 
     22 PlatformUIThread::~PlatformUIThread() {
     23  CritScope scoped_lock(&cs_);
     24  switch (state_) {
     25    case State::STARTED: {
     26      MOZ_DIAGNOSTIC_ASSERT(
     27          false, "PlatformUIThread must be stopped before destruction");
     28      break;
     29    }
     30    case State::STOPPED:
     31      break;
     32    case State::UNSTARTED:
     33      break;
     34  }
     35 }
     36 
     37 bool PlatformUIThread::InternalInit() {
     38  // Create an event window for use in generating callbacks to capture
     39  // objects.
     40  CritScope scoped_lock(&cs_);
     41  switch (state_) {
     42    // We have already started there is nothing todo. Should this be assert?
     43    case State::STARTED:
     44      break;
     45    // Stop() has already been called so there is likewise nothing to do.
     46    case State::STOPPED:
     47      break;
     48    // Stop() has not been called yet, setup the UI thread, and set our
     49    // state to STARTED.
     50    case State::UNSTARTED: {
     51      WNDCLASSW wc;
     52      HMODULE hModule = GetModuleHandle(NULL);
     53      if (!GetClassInfoW(hModule, kThreadWindow, &wc)) {
     54        ZeroMemory(&wc, sizeof(WNDCLASSW));
     55        wc.hInstance = hModule;
     56        wc.lpfnWndProc = EventWindowProc;
     57        wc.lpszClassName = kThreadWindow;
     58        RegisterClassW(&wc);
     59      }
     60      hwnd_ = CreateWindowW(kThreadWindow, L"", 0, 0, 0, 0, 0, NULL, NULL,
     61                            hModule, NULL);
     62      // Added in review of bug 1760843, follow up to remove 1767861
     63      MOZ_RELEASE_ASSERT(hwnd_);
     64      // Expected to always work but if it doesn't we should still fulfill the
     65      // contract of always running the process loop at least a single
     66      // iteration.
     67      // This could be rexamined in the future.
     68      if (hwnd_) {
     69        SetPropW(hwnd_, kThisProperty, this);
     70        // state_ needs to be STARTED before we request the initial timer
     71        state_ = State::STARTED;
     72        if (timeout_) {
     73          // if someone set the timer before we started
     74          RequestCallbackTimer(timeout_);
     75        }
     76      }
     77      break;
     78    }
     79  };
     80  return state_ == State::STARTED;
     81 }
     82 
     83 bool PlatformUIThread::RequestCallbackTimer(unsigned int milliseconds) {
     84  CritScope scoped_lock(&cs_);
     85 
     86  switch (state_) {
     87    // InternalInit() has yet to run so we do not have a UI thread to use as a
     88    // target of the timer. We should just remember what timer interval was
     89    // requested and let InternalInit() call this function again when it is
     90    // ready.
     91    case State::UNSTARTED: {
     92      timeout_ = milliseconds;
     93      return false;
     94    }
     95    // We have already stopped, do not schedule a new timer.
     96    case State::STOPPED:
     97      return false;
     98    case State::STARTED: {
     99      if (timerid_) {
    100        KillTimer(hwnd_, timerid_);
    101      }
    102      timeout_ = milliseconds;
    103      timerid_ = SetTimer(hwnd_, kTimerId, milliseconds, NULL);
    104      return !!timerid_;
    105    }
    106  }
    107  // UNREACHABLE
    108 }
    109 
    110 void PlatformUIThread::Stop() {
    111  {
    112    RTC_DCHECK_RUN_ON(&thread_checker_);
    113    CritScope scoped_lock(&cs_);
    114    // Shut down the dispatch loop and let the background thread exit.
    115    if (timerid_) {
    116      MOZ_ASSERT(hwnd_);
    117      KillTimer(hwnd_, timerid_);
    118      timerid_ = 0;
    119    }
    120    switch (state_) {
    121      // If we haven't started yet there is nothing to do, we will go into
    122      // the STOPPED state at the end of the function and InternalInit()
    123      // will not move us to STARTED.
    124      case State::UNSTARTED:
    125        break;
    126      // If we have started, that means that InternalInit() has run and the
    127      // message wait loop has or will run. We need to signal it to stop. wich
    128      // will allow PlatformThread::Stop to join that thread.
    129      case State::STARTED: {
    130        MOZ_ASSERT(hwnd_);
    131        PostMessage(hwnd_, WM_CLOSE, 0, 0);
    132        break;
    133      }
    134      // We have already stopped. There is nothing to do.
    135      case State::STOPPED:
    136        break;
    137    }
    138    // Always set our state to STOPPED
    139    state_ = State::STOPPED;
    140  }
    141  monitor_thread_.Finalize();
    142 }
    143 
    144 void PlatformUIThread::Run() {
    145  // InternalInit() will return false when the thread is already in shutdown.
    146  // otherwise we must run until we get a Windows WM_QUIT msg.
    147  const bool runUntilQuitMsg = InternalInit();
    148  // The interface contract of Start/Stop is that for a successful call to
    149  // Start, there should be at least one call to the run function.
    150  NativeEventCallback();
    151  while (runUntilQuitMsg) {
    152    // Alertable sleep to receive WM_QUIT (following a WM_CLOSE triggering a
    153    // WM_DESTROY)
    154    if (MsgWaitForMultipleObjectsEx(0, nullptr, INFINITE, QS_ALLINPUT,
    155                                    MWMO_ALERTABLE | MWMO_INPUTAVAILABLE) ==
    156        WAIT_OBJECT_0) {
    157      MSG msg;
    158      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
    159        if (msg.message == WM_QUIT) {
    160          // THE ONLY WAY to exit the thread loop
    161          break;
    162        }
    163        TranslateMessage(&msg);
    164        DispatchMessage(&msg);
    165      }
    166    }
    167  }
    168 }
    169 
    170 void PlatformUIThread::NativeEventCallback() { native_event_callback_(); }
    171 
    172 /* static */
    173 LRESULT CALLBACK PlatformUIThread::EventWindowProc(HWND hwnd, UINT uMsg,
    174                                                   WPARAM wParam,
    175                                                   LPARAM lParam) {
    176  if (uMsg == WM_DESTROY) {
    177    RemovePropW(hwnd, kThisProperty);
    178    PostQuitMessage(0);
    179    return 0;
    180  }
    181 
    182  PlatformUIThread* twui =
    183      static_cast<PlatformUIThread*>(GetPropW(hwnd, kThisProperty));
    184  if (!twui) {
    185    return DefWindowProc(hwnd, uMsg, wParam, lParam);
    186  }
    187 
    188  if (uMsg == WM_TIMER && wParam == kTimerId) {
    189    twui->NativeEventCallback();
    190    return 0;
    191  }
    192 
    193  return DefWindowProc(hwnd, uMsg, wParam, lParam);
    194 }
    195 
    196 }  // namespace webrtc
    197 
    198 #endif