text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef CONTENT_RENDERER_PEPPER_PEPPER_HUNG_PLUGIN_FILTER_H_
  #define CONTENT_RENDERER_PEPPER_PEPPER_HUNG_PLUGIN_FILTER_H_
  
  #include "base/memory/ref_counted.h"
  #include "base/synchronization/lock.h"
  #include "content/common/pepper_plugin.mojom.h"
  #include "ipc/ipc_channel_proxy.h"
  #include "ipc/ipc_sync_message_filter.h"
  #include "mojo/public/cpp/bindings/pending_remote.h"
  #include "mojo/public/cpp/bindings/remote.h"
  #include "ppapi/proxy/host_dispatcher.h"
  
  namespace content {
  
  // This class monitors a renderer <-> pepper plugin channel on the I/O thread
  // of the renderer for a hung plugin.
  //
  // If the plugin is not responding to sync messages, it will notify the browser
  // process and give the user the option to kill the hung plugin.
  //
  // Note that this class must be threadsafe since it will get the begin/end
  // block notifications on the main thread, but the filter is run on the I/O
  // thread. This is important since when we're blocked on a sync message to a
  // hung plugin, the main thread is frozen.
  //
  // NOTE: This class is refcounted (via IPC::MessageFilter).
  class PepperHungPluginFilter
      : public ppapi::proxy::HostDispatcher::SyncMessageStatusObserver,
        public IPC::MessageFilter {
   public:
    PepperHungPluginFilter();
  
    PepperHungPluginFilter(const PepperHungPluginFilter&) = delete;
    PepperHungPluginFilter& operator=(const PepperHungPluginFilter&) = delete;
  
    // The |hung_host| is the mojo channel to inform the browser about the new
    // hung status.
    void BindHungDetectorHost(
        mojo::PendingRemote<mojom::PepperHungDetectorHost> hung_host);
  
    // SyncMessageStatusReceiver implementation.
    void BeginBlockOnSyncMessage() override;
    void EndBlockOnSyncMessage() override;
  
    // MessageFilter implementation.
    void OnFilterRemoved() override;
    void OnChannelError() override;
    bool OnMessageReceived(const IPC::Message& message) override;
  
    // Notification the HostDispatcher on the main thread has been destroyed.
    void HostDispatcherDestroyed();
  
   protected:
    ~PepperHungPluginFilter() override;
  
   private:
    // Binds the mojo channel on the IO thread (where it will be used).
    void BindHungDetectorHostOnIOThread(
        mojo::PendingRemote<mojom::PepperHungDetectorHost> hung_host);
  
    // Unbinds the mojo channel on the IO thread.
    void UnbindHungDetectorHostOnIOThread();
  
    // Makes sure that the hung timer is scheduled.
    void EnsureTimerScheduled();
  
    // Checks whether the plugin could have transitioned from hung to unhung and
    // notifies the browser if so.
    void MayHaveBecomeUnhung();
  
    // Calculate the point at which the plugin could next be considered hung.
    base::TimeTicks GetHungTime() const;
  
    // Checks if the plugin is considered hung based on whether it has been
    // blocked for long enough.
    bool IsHung() const;
  
    // Timer handler that checks for a hang after a timeout.
    void OnHangTimer();
  
    // Sends the hung/unhung message to the browser process.
    void SendHungMessage(bool is_hung);
  
    base::Lock lock_;
  
    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
  
    // The channel back to the browser for informing it of the new status.
    // This remote is bound and used on the IO thread.
    mojo::Remote<mojom::PepperHungDetectorHost> hung_host_;
  
    // The time when we start being blocked on a sync message. If there are
    // nested ones, this is the time of the outermost one.
    //
    // This will be is_null() if we've never blocked.
    base::TimeTicks began_blocking_time_;
  
    // Time that the last message was received from the plugin.
    //
    // This will be is_null() if we've never received any messages.
    base::TimeTicks last_message_received_;
  
    // Number of nested sync messages that we're blocked on.
    int pending_sync_message_count_ = 0;
  
    // True when we've sent the "plugin is hung" message to the browser. We track
    // this so we know to look for it becoming unhung and send the corresponding
    // message to the browser.
    bool hung_plugin_showing_ = false;
  
    bool timer_task_pending_ = false;
  };
  
  }  // namespace content
  
  #endif  // CONTENT_RENDERER_PEPPER_PEPPER_HUNG_PLUGIN_FILTER_H_
  