File: hung_plugin_tab_helper.cc

package info (click to toggle)
chromium 138.0.7204.157-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,864 kB
  • sloc: cpp: 34,936,859; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,967; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (188 lines) | stat: -rw-r--r-- 6,976 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/hung_plugin_tab_helper.h"

#include <algorithm>
#include <memory>

#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/process/process.h"
#include "build/build_config.h"
#include "chrome/browser/hang_monitor/hang_crash_dump.h"
#include "chrome/browser/plugins/hung_plugin_infobar_delegate.h"
#include "chrome/common/channel_info.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/infobars/core/infobar.h"
#include "content/public/browser/browser_child_process_host_iterator.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/common/process_type.h"
#include "content/public/common/result_codes.h"

// HungPluginTabHelper::PluginState -------------------------------------------

// Per-plugin state (since there could be more than one plugin hung).  The
// integer key is the child process ID of the plugin process.  This maintains
// the state for all plugins on this page that are currently hung, whether or
// not we're currently showing the infobar.
struct HungPluginTabHelper::PluginState {
  // Initializes the plugin state to be a hung plugin.
  PluginState(const base::FilePath& path, const std::u16string& name);

  // Since the scope of the timer manages our callback, this struct should
  // not be copied.
  PluginState(const PluginState&) = delete;
  PluginState& operator=(const PluginState&) = delete;

  ~PluginState() = default;

  base::FilePath path;
  std::u16string name;

  // Possibly-null if we're not showing an infobar right now.
  raw_ptr<infobars::InfoBar> infobar = nullptr;

  // Time to delay before re-showing the infobar for a hung plugin. This is
  // increased each time the user cancels it.
  base::TimeDelta next_reshow_delay = base::Seconds(10);

  // Handles calling the helper when the infobar should be re-shown.
  base::OneShotTimer timer;
};

HungPluginTabHelper::PluginState::PluginState(const base::FilePath& path,
                                              const std::u16string& name)
    : path(path), name(name) {}

// HungPluginTabHelper --------------------------------------------------------

HungPluginTabHelper::~HungPluginTabHelper() = default;

void HungPluginTabHelper::PluginCrashed(const base::FilePath& plugin_path,
                                        base::ProcessId plugin_pid) {
  // For now, just do a brute-force search to see if we have this plugin. Since
  // we'll normally have 0 or 1, this is fast.
  const auto i =
      std::ranges::find(hung_plugins_, plugin_path,
                        [](const auto& elem) { return elem.second->path; });
  if (i != hung_plugins_.end()) {
    if (i->second->infobar) {
      infobars::ContentInfoBarManager* infobar_manager =
          infobars::ContentInfoBarManager::FromWebContents(web_contents());
      if (infobar_manager) {
        infobar_manager->RemoveInfoBar(i->second->infobar);
      }
    }
    hung_plugins_.erase(i);
  }
}

void HungPluginTabHelper::PluginHungStatusChanged(
    int plugin_child_id,
    const base::FilePath& plugin_path,
    bool is_hung) {
  infobars::ContentInfoBarManager* infobar_manager =
      infobars::ContentInfoBarManager::FromWebContents(web_contents());

  auto found = hung_plugins_.find(plugin_child_id);
  if (found != hung_plugins_.end()) {
    if (!is_hung) {
      // Hung plugin became un-hung, close the infobar and delete our info.
      if (found->second->infobar && infobar_manager) {
        infobar_manager->RemoveInfoBar(found->second->infobar);
      }
      hung_plugins_.erase(found);
    }
    return;
  }

  if (!infobar_manager) {
    return;
  }
  if (!infobar_observations_.IsObservingSource(infobar_manager)) {
    infobar_observations_.AddObservation(infobar_manager);
  }

  std::u16string plugin_name =
      content::PluginService::GetInstance()->GetPluginDisplayNameByPath(
          plugin_path);
  hung_plugins_[plugin_child_id] =
      std::make_unique<PluginState>(plugin_path, plugin_name);
  ShowBar(plugin_child_id, hung_plugins_[plugin_child_id].get());
}

void HungPluginTabHelper::OnInfoBarRemoved(infobars::InfoBar* infobar,
                                           bool animate) {
  const auto i =
      std::ranges::find(hung_plugins_, infobar,
                        [](const auto& elem) { return elem.second->infobar; });
  if (i != hung_plugins_.end()) {
    PluginState* state = i->second.get();
    state->infobar = nullptr;

    // Schedule the timer to re-show the infobar if the plugin continues to be
    // hung.
    state->timer.Start(FROM_HERE, state->next_reshow_delay,
                       base::BindOnce(&HungPluginTabHelper::OnReshowTimer,
                                      base::Unretained(this), i->first));

    // Next time we do this, delay it twice as long to avoid being annoying.
    state->next_reshow_delay *= 2;
  }
}

void HungPluginTabHelper::OnManagerShuttingDown(
    infobars::InfoBarManager* manager) {
  infobar_observations_.RemoveObservation(manager);
}

void HungPluginTabHelper::KillPlugin(int child_id) {
  // Be careful with the child_id. It's supplied by the renderer which might be
  // hacked.
  content::BrowserChildProcessHostIterator iter(
      content::PROCESS_TYPE_PPAPI_PLUGIN);
  while (!iter.Done()) {
    const content::ChildProcessData& data = iter.GetData();
    if (data.id == child_id) {
      CrashDumpHungChildProcess(data.GetProcess().Handle());
      data.GetProcess().Terminate(content::RESULT_CODE_HUNG, false);
      return;
    }
    ++iter;
  }
  // Ignore the case where we didn't find the plugin, it may have terminated
  // before this function could run.
}

HungPluginTabHelper::HungPluginTabHelper(content::WebContents* contents)
    : content::WebContentsObserver(contents),
      content::WebContentsUserData<HungPluginTabHelper>(*contents) {}

void HungPluginTabHelper::OnReshowTimer(int child_id) {
  // The timer should have been cancelled if the record isn't in our map
  // anymore.
  auto found = hung_plugins_.find(child_id);
  CHECK(found != hung_plugins_.end());
  DCHECK(!found->second->infobar);
  ShowBar(child_id, found->second.get());
}

void HungPluginTabHelper::ShowBar(int child_id, PluginState* state) {
  infobars::ContentInfoBarManager* infobar_manager =
      infobars::ContentInfoBarManager::FromWebContents(web_contents());
  if (!infobar_manager) {
    return;
  }

  DCHECK(!state->infobar);
  state->infobar = HungPluginInfoBarDelegate::Create(infobar_manager, this,
                                                     child_id, state->name);
}

WEB_CONTENTS_USER_DATA_KEY_IMPL(HungPluginTabHelper);