15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h>
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/hang_monitor/hung_plugin_action.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/version.h"
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/ui/simple_message_box.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/logging_chrome.h"
13ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "content/public/browser/plugin_service.h"
14ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "content/public/common/webplugininfo.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
17d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/win/hwnd_util.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kGTalkPluginName[] = L"Google Talk Plugin";
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kGTalkPluginLogMinVersion = 26;  // For version 2.6 and below.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum GTalkPluginLogVersion {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GTALK_PLUGIN_VERSION_MIN = 0,
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GTALK_PLUGIN_VERSION_27,
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GTALK_PLUGIN_VERSION_28,
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GTALK_PLUGIN_VERSION_29,
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GTALK_PLUGIN_VERSION_30,
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GTALK_PLUGIN_VERSION_31,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GTALK_PLUGIN_VERSION_32,
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GTALK_PLUGIN_VERSION_33,
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GTALK_PLUGIN_VERSION_34,
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GTALK_PLUGIN_VERSION_MAX
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Converts the version string of Google Talk Plugin to a version enum. The
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// version format is "major(1 digit).minor(1 digit).sub(1 or 2 digits)",
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// for example, "2.7.10" and "2.8.1". Converts the string to a number as
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 10 * major + minor - kGTalkPluginLogMinVersion.
41a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)GTalkPluginLogVersion GetGTalkPluginVersion(const base::string16& version) {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int gtalk_plugin_version = GTALK_PLUGIN_VERSION_MIN;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Version plugin_version;
44ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  content::WebPluginInfo::CreateVersionFromString(version, &plugin_version);
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (plugin_version.IsValid() && plugin_version.components().size() >= 2) {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtalk_plugin_version = 10 * plugin_version.components()[0] +
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        plugin_version.components()[1] - kGTalkPluginLogMinVersion;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (gtalk_plugin_version < GTALK_PLUGIN_VERSION_MIN)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return GTALK_PLUGIN_VERSION_MIN;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (gtalk_plugin_version > GTALK_PLUGIN_VERSION_MAX)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return GTALK_PLUGIN_VERSION_MAX;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<GTalkPluginLogVersion>(gtalk_plugin_version);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HungPluginAction::HungPluginAction() : current_hung_plugin_window_(NULL) {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HungPluginAction::~HungPluginAction() {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool HungPluginAction::OnHungWindowDetected(HWND hung_window,
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            HWND top_level_window,
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            ActionOnHungWindow* action) {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (NULL == action) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsWindow(hung_window)) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool continue_hang_detection = true;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD hung_window_process_id = 0;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD top_level_window_process_id = 0;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetWindowThreadProcessId(hung_window, &hung_window_process_id);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetWindowThreadProcessId(top_level_window, &top_level_window_process_id);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *action = HungWindowNotification::HUNG_WINDOW_IGNORE;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (top_level_window_process_id != hung_window_process_id) {
84a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16 plugin_name;
85a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16 plugin_version;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetPluginNameAndVersion(hung_window,
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            top_level_window_process_id,
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &plugin_name,
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &plugin_version);
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (plugin_name.empty()) {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      plugin_name = l10n_util::GetStringUTF16(IDS_UNKNOWN_PLUGIN_NAME);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (kGTalkPluginName == plugin_name) {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UMA_HISTOGRAM_ENUMERATION("GTalkPlugin.Hung",
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetGTalkPluginVersion(plugin_version),
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GTALK_PLUGIN_VERSION_MAX + 1);
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (logging::DialogsAreSuppressed()) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED() << "Terminated a hung plugin process.";
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
102a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      const base::string16 title = l10n_util::GetStringUTF16(
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          IDS_BROWSER_HANGMONITOR_TITLE);
104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      const base::string16 message = l10n_util::GetStringFUTF16(
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          IDS_BROWSER_HANGMONITOR, plugin_name);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Before displaying the message box, invoke SendMessageCallback on the
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // hung window. If the callback ever hits, the window is not hung anymore
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // and we can dismiss the message box.
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SendMessageCallback(hung_window,
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          WM_NULL,
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          0,
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          0,
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          HungWindowResponseCallback,
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          reinterpret_cast<ULONG_PTR>(this));
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_hung_plugin_window_ = hung_window;
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (chrome::ShowMessageBox(
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)              NULL, title, message, chrome::MESSAGE_BOX_TYPE_QUESTION) ==
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          chrome::MESSAGE_BOX_RESULT_YES) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If the user choses to ignore the hung window warning, the
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // message timeout for this window should be doubled. We only
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // double the timeout property on the window if the property
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // exists. The property is deleted if the window becomes
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // responsive.
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue_hang_detection = false;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma warning(disable:4311)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        int child_window_message_timeout =
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            reinterpret_cast<int>(GetProp(
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                hung_window, HungWindowDetector::kHungChildWindowTimeout));
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma warning(default:4311)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (child_window_message_timeout) {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          child_window_message_timeout *= 2;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma warning(disable:4312)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          SetProp(hung_window, HungWindowDetector::kHungChildWindowTimeout,
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  reinterpret_cast<HANDLE>(child_window_message_timeout));
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma warning(default:4312)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_hung_plugin_window_ = NULL;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS == *action) {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Enable the top-level window just in case the plugin had been
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // displaying a modal box that had disabled the top-level window
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnableWindow(top_level_window, TRUE);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return continue_hang_detection;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HungPluginAction::OnWindowResponsive(HWND window) {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (window == current_hung_plugin_window_) {
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The message timeout for this window should fallback to the default
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // timeout as this window is now responsive.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RemoveProp(window, HungWindowDetector::kHungChildWindowTimeout);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The monitored plugin recovered. Let's dismiss the message box.
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnumThreadWindows(GetCurrentThreadId(),
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      reinterpret_cast<WNDENUMPROC>(DismissMessageBox),
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      NULL);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool HungPluginAction::GetPluginNameAndVersion(HWND plugin_window,
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                               DWORD browser_process_id,
165a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                               base::string16* plugin_name,
166a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                               base::string16* plugin_version) {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(plugin_name);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(plugin_version);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HWND window_to_check = plugin_window;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (NULL != window_to_check) {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DWORD process_id = 0;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetWindowThreadProcessId(window_to_check, &process_id);
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (process_id == browser_process_id) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If we have reached a window the that belongs to the browser process
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // we have gone too far.
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
178ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if (content::PluginService::GetInstance()->GetPluginInfoFromWindow(
179ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch            window_to_check, plugin_name, plugin_version)) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window_to_check = GetParent(window_to_check);
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BOOL CALLBACK HungPluginAction::DismissMessageBox(HWND window, LPARAM ignore) {
189a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 class_name = gfx::GetClassName(window);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // #32770 is the dialog window class which is the window class of
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the message box being displayed.
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (class_name == L"#32770") {
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EndDialog(window, IDNO);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return FALSE;
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return TRUE;
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CALLBACK HungPluginAction::HungWindowResponseCallback(HWND target_window,
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           UINT message,
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           ULONG_PTR data,
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           LRESULT result) {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HungPluginAction* instance = reinterpret_cast<HungPluginAction*>(data);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(NULL != instance);
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (NULL != instance) {
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    instance->OnWindowResponsive(target_window);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
210