Skip to content

Commit

Permalink
Log when a browser plugin hangs
Browse files Browse the repository at this point in the history
Add an API to webplugin_delegate_impl to get the plugin version from the window, similar to the way to get the plugin name. Update hung_plugin_action to log when a browser plugin hangs and log the version of the plugin is Google Talk Plugin. 

BUG=
TEST=try bot and manual test on Google Talk Plugin hangs


Review URL: http://codereview.chromium.org/9958104

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133292 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
whyuan@google.com committed Apr 20, 2012
1 parent 4386be5 commit f62f2c5
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 35 deletions.
76 changes: 65 additions & 11 deletions chrome/browser/hang_monitor/hung_plugin_action.cc
Original file line number Diff line number Diff line change
@@ -1,18 +1,61 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// 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.

#include <windows.h>

#include "chrome/browser/hang_monitor/hung_plugin_action.h"

#include "base/metrics/histogram.h"
#include "base/version.h"
#include "chrome/browser/simple_message_box.h"
#include "chrome/common/logging_chrome.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/win/hwnd_util.h"
#include "webkit/plugins/npapi/plugin_group.h"
#include "webkit/plugins/npapi/webplugin_delegate_impl.h"

namespace {

const wchar_t kGTalkPluginName[] = L"Google Talk Plugin";
const int kGTalkPluginLogMinVersion = 26; // For version 2.6 and below.

enum GTalkPluginLogVersion {
GTALK_PLUGIN_VERSION_MIN = 0,
GTALK_PLUGIN_VERSION_27,
GTALK_PLUGIN_VERSION_28,
GTALK_PLUGIN_VERSION_29,
GTALK_PLUGIN_VERSION_30,
GTALK_PLUGIN_VERSION_31,
GTALK_PLUGIN_VERSION_32,
GTALK_PLUGIN_VERSION_33,
GTALK_PLUGIN_VERSION_34,
GTALK_PLUGIN_VERSION_MAX
};

// Converts the version string of Google Talk Plugin to a version enum. The
// version format is "major(1 digit).minor(1 digit).sub(1 or 2 digits)",
// for example, "2.7.10" and "2.8.1". Converts the string to a number as
// 10 * major + minor - kGTalkPluginLogMinVersion.
GTalkPluginLogVersion GetGTalkPluginVersion(const string16& version) {
int gtalk_plugin_version = GTALK_PLUGIN_VERSION_MIN;
scoped_ptr<Version> plugin_version(
webkit::npapi::PluginGroup::CreateVersionFromString(version));
if (plugin_version.get() && plugin_version->components().size() >= 2) {
gtalk_plugin_version = 10 * plugin_version->components()[0] +
plugin_version->components()[1] - kGTalkPluginLogMinVersion;
}

if (gtalk_plugin_version < GTALK_PLUGIN_VERSION_MIN)
return GTALK_PLUGIN_VERSION_MIN;
if (gtalk_plugin_version > GTALK_PLUGIN_VERSION_MAX)
return GTALK_PLUGIN_VERSION_MAX;
return static_cast<GTalkPluginLogVersion>(gtalk_plugin_version);
}

} // namespace

HungPluginAction::HungPluginAction() : current_hung_plugin_window_(NULL) {
}

Expand All @@ -38,17 +81,24 @@ bool HungPluginAction::OnHungWindowDetected(HWND hung_window,

*action = HungWindowNotification::HUNG_WINDOW_IGNORE;
if (top_level_window_process_id != hung_window_process_id) {
string16 plugin_name;
string16 plugin_version;
GetPluginNameAndVersion(hung_window,
top_level_window_process_id,
&plugin_name,
&plugin_version);
if (plugin_name.empty()) {
plugin_name = l10n_util::GetStringUTF16(IDS_UNKNOWN_PLUGIN_NAME);
} else if (kGTalkPluginName == plugin_name) {
UMA_HISTOGRAM_ENUMERATION("GTalkPlugin.Hung",
GetGTalkPluginVersion(plugin_version),
GTALK_PLUGIN_VERSION_MAX + 1);
}

if (logging::DialogsAreSuppressed()) {
NOTREACHED() << "Terminated a hung plugin process.";
*action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
} else {
string16 plugin_name;
GetPluginName(hung_window,
top_level_window_process_id,
&plugin_name);
if (plugin_name.empty()) {
plugin_name = l10n_util::GetStringUTF16(IDS_UNKNOWN_PLUGIN_NAME);
}
string16 msg = l10n_util::GetStringFUTF16(IDS_BROWSER_HANGMONITOR,
plugin_name);
string16 title = l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_TITLE);
Expand Down Expand Up @@ -107,10 +157,12 @@ void HungPluginAction::OnWindowResponsive(HWND window) {
}
}

bool HungPluginAction::GetPluginName(HWND plugin_window,
DWORD browser_process_id,
std::wstring* plugin_name) {
bool HungPluginAction::GetPluginNameAndVersion(HWND plugin_window,
DWORD browser_process_id,
string16* plugin_name,
string16* plugin_version) {
DCHECK(plugin_name);
DCHECK(plugin_version);
HWND window_to_check = plugin_window;
while (NULL != window_to_check) {
DWORD process_id = 0;
Expand All @@ -122,6 +174,8 @@ bool HungPluginAction::GetPluginName(HWND plugin_window,
}
if (webkit::npapi::WebPluginDelegateImpl::GetPluginNameFromWindow(
window_to_check, plugin_name)) {
webkit::npapi::WebPluginDelegateImpl::GetPluginVersionFromWindow(
window_to_check, plugin_version);
return true;
}
window_to_check = GetParent(window_to_check);
Expand Down
11 changes: 7 additions & 4 deletions chrome/browser/hang_monitor/hung_plugin_action.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// 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 CHROME_BROWSER_HANG_MONITOR_HUNG_PLUGIN_ACTION_H__
#define CHROME_BROWSER_HANG_MONITOR_HUNG_PLUGIN_ACTION_H__
#pragma once

#include "base/string16.h"
#include "chrome/browser/hang_monitor/hung_window_detector.h"

// This class provides an implementation the
// HungWindowDetector::HungWindowNotification callback interface.
// It checks to see if the hung window belongs to a process different
Expand Down Expand Up @@ -36,9 +38,10 @@ class HungPluginAction : public HungWindowDetector::HungWindowNotification {
static BOOL CALLBACK DismissMessageBox(HWND window, LPARAM ignore);

protected:
bool GetPluginName(HWND plugin_window,
DWORD browser_process_id,
std::wstring *plugin_name);
bool GetPluginNameAndVersion(HWND plugin_window,
DWORD browser_process_id,
string16* plugin_name,
string16* plugin_version);
// The currently hung plugin window that we are prompting the user about
HWND current_hung_plugin_window_;
};
Expand Down
2 changes: 2 additions & 0 deletions webkit/plugins/npapi/webplugin_delegate_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ class WEBKIT_PLUGINS_EXPORT WebPluginDelegateImpl : public WebPluginDelegate {
static bool IsPluginDelegateWindow(gfx::NativeWindow window);
static bool GetPluginNameFromWindow(gfx::NativeWindow window,
string16* plugin_name);
static bool GetPluginVersionFromWindow(gfx::NativeWindow window,
string16* plugin_version);

// Returns true if the window handle passed in is that of the dummy
// activation window for windowless plugins.
Expand Down
65 changes: 45 additions & 20 deletions webkit/plugins/npapi/webplugin_delegate_impl_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace {

const wchar_t kWebPluginDelegateProperty[] = L"WebPluginDelegateProperty";
const wchar_t kPluginNameAtomProperty[] = L"PluginNameAtom";
const wchar_t kPluginVersionAtomProperty[] = L"PluginVersionAtom";
const wchar_t kDummyActivationWindowName[] = L"DummyWindowForActivation";
const wchar_t kPluginFlashThrottle[] = L"FlashThrottle";

Expand Down Expand Up @@ -263,6 +264,22 @@ int GetPluginMajorVersion(const WebPluginInfo& plugin_info) {
return major_version;
}

bool GetPluginPropertyFromWindow(
HWND window, const wchar_t* plugin_atom_property,
string16* plugin_property) {
ATOM plugin_atom = reinterpret_cast<ATOM>(
GetPropW(window, plugin_atom_property));
if (plugin_atom != 0) {
WCHAR plugin_property_local[MAX_PATH] = {0};
GlobalGetAtomNameW(plugin_atom,
plugin_property_local,
ARRAYSIZE(plugin_property_local));
*plugin_property = plugin_property_local;
return true;
}
return false;
}

} // namespace

bool WebPluginDelegateImpl::IsPluginDelegateWindow(HWND window) {
Expand All @@ -276,23 +293,17 @@ bool WebPluginDelegateImpl::IsPluginDelegateWindow(HWND window) {
// static
bool WebPluginDelegateImpl::GetPluginNameFromWindow(
HWND window, string16* plugin_name) {
if (NULL == plugin_name) {
return false;
}
if (!IsPluginDelegateWindow(window)) {
return false;
}
ATOM plugin_name_atom = reinterpret_cast<ATOM>(
GetPropW(window, kPluginNameAtomProperty));
if (plugin_name_atom != 0) {
WCHAR plugin_name_local[MAX_PATH] = {0};
GlobalGetAtomNameW(plugin_name_atom,
plugin_name_local,
ARRAYSIZE(plugin_name_local));
*plugin_name = plugin_name_local;
return true;
}
return false;
return IsPluginDelegateWindow(window) &&
GetPluginPropertyFromWindow(
window, kPluginNameAtomProperty, plugin_name);
}

// static
bool WebPluginDelegateImpl::GetPluginVersionFromWindow(
HWND window, string16* plugin_version) {
return IsPluginDelegateWindow(window) &&
GetPluginPropertyFromWindow(
window, kPluginVersionAtomProperty, plugin_version);
}

bool WebPluginDelegateImpl::IsDummyActivationWindow(HWND window) {
Expand Down Expand Up @@ -695,9 +706,9 @@ bool WebPluginDelegateImpl::WindowedCreatePlugin() {

BOOL result = SetProp(windowed_handle_, kWebPluginDelegateProperty, this);
DCHECK(result == TRUE) << "SetProp failed, last error = " << GetLastError();
// Get the name of the plugin, create an atom and set that in a window
// property. Use an atom so that other processes can access the name of
// the plugin that this window is hosting
// Get the name and version of the plugin, create atoms and set them in a
// window property. Use atoms so that other processes can access the name and
// version of the plugin that this window is hosting.
if (instance_ != NULL) {
PluginLib* plugin_lib = instance()->plugin_lib();
if (plugin_lib != NULL) {
Expand All @@ -711,6 +722,16 @@ bool WebPluginDelegateImpl::WindowedCreatePlugin() {
DCHECK(result == TRUE) << "SetProp failed, last error = " <<
GetLastError();
}
string16 plugin_version = plugin_lib->plugin_info().version;
if (!plugin_version.empty()) {
ATOM plugin_version_atom = GlobalAddAtomW(plugin_version.c_str());
DCHECK(0 != plugin_version_atom);
result = SetProp(windowed_handle_,
kPluginVersionAtomProperty,
reinterpret_cast<HANDLE>(plugin_version_atom));
DCHECK(result == TRUE) << "SetProp failed, last error = " <<
GetLastError();
}
}
}

Expand Down Expand Up @@ -1207,6 +1228,10 @@ LRESULT CALLBACK WebPluginDelegateImpl::NativeWndProc(
RemoveProp(hwnd, kPluginNameAtomProperty));
if (plugin_name_atom != 0)
GlobalDeleteAtom(plugin_name_atom);
ATOM plugin_version_atom = reinterpret_cast<ATOM>(
RemoveProp(hwnd, kPluginVersionAtomProperty));
if (plugin_version_atom != 0)
GlobalDeleteAtom(plugin_version_atom);
ClearThrottleQueueForWindow(hwnd);
}
}
Expand Down

0 comments on commit f62f2c5

Please sign in to comment.