Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / hang_monitor / hung_plugin_action.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <windows.h>
6
7 #include "chrome/browser/hang_monitor/hung_plugin_action.h"
8
9 #include "base/metrics/histogram.h"
10 #include "base/version.h"
11 #include "chrome/browser/ui/simple_message_box.h"
12 #include "chrome/common/logging_chrome.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "content/public/browser/plugin_service.h"
15 #include "content/public/common/webplugininfo.h"
16 #include "ui/base/l10n/l10n_util.h"
17 #include "ui/gfx/win/hwnd_util.h"
18
19 namespace {
20
21 const wchar_t kGTalkPluginName[] = L"Google Talk Plugin";
22 const int kGTalkPluginLogMinVersion = 26;  // For version 2.6 and below.
23
24 enum GTalkPluginLogVersion {
25   GTALK_PLUGIN_VERSION_MIN = 0,
26   GTALK_PLUGIN_VERSION_27,
27   GTALK_PLUGIN_VERSION_28,
28   GTALK_PLUGIN_VERSION_29,
29   GTALK_PLUGIN_VERSION_30,
30   GTALK_PLUGIN_VERSION_31,
31   GTALK_PLUGIN_VERSION_32,
32   GTALK_PLUGIN_VERSION_33,
33   GTALK_PLUGIN_VERSION_34,
34   GTALK_PLUGIN_VERSION_MAX
35 };
36
37 // Converts the version string of Google Talk Plugin to a version enum. The
38 // version format is "major(1 digit).minor(1 digit).sub(1 or 2 digits)",
39 // for example, "2.7.10" and "2.8.1". Converts the string to a number as
40 // 10 * major + minor - kGTalkPluginLogMinVersion.
41 GTalkPluginLogVersion GetGTalkPluginVersion(const base::string16& version) {
42   int gtalk_plugin_version = GTALK_PLUGIN_VERSION_MIN;
43   Version plugin_version;
44   content::WebPluginInfo::CreateVersionFromString(version, &plugin_version);
45   if (plugin_version.IsValid() && plugin_version.components().size() >= 2) {
46     gtalk_plugin_version = 10 * plugin_version.components()[0] +
47         plugin_version.components()[1] - kGTalkPluginLogMinVersion;
48   }
49
50   if (gtalk_plugin_version < GTALK_PLUGIN_VERSION_MIN)
51     return GTALK_PLUGIN_VERSION_MIN;
52   if (gtalk_plugin_version > GTALK_PLUGIN_VERSION_MAX)
53     return GTALK_PLUGIN_VERSION_MAX;
54   return static_cast<GTalkPluginLogVersion>(gtalk_plugin_version);
55 }
56
57 }  // namespace
58
59 HungPluginAction::HungPluginAction() : current_hung_plugin_window_(NULL) {
60 }
61
62 HungPluginAction::~HungPluginAction() {
63 }
64
65 bool HungPluginAction::OnHungWindowDetected(HWND hung_window,
66                                             HWND top_level_window,
67                                             ActionOnHungWindow* action) {
68   if (NULL == action) {
69     return false;
70   }
71   if (!IsWindow(hung_window)) {
72     return false;
73   }
74
75   bool continue_hang_detection = true;
76
77   DWORD hung_window_process_id = 0;
78   DWORD top_level_window_process_id = 0;
79   GetWindowThreadProcessId(hung_window, &hung_window_process_id);
80   GetWindowThreadProcessId(top_level_window, &top_level_window_process_id);
81
82   *action = HungWindowNotification::HUNG_WINDOW_IGNORE;
83   if (top_level_window_process_id != hung_window_process_id) {
84     base::string16 plugin_name;
85     base::string16 plugin_version;
86
87     content::PluginService::GetInstance()->GetPluginInfoFromWindow(
88         hung_window, &plugin_name, &plugin_version);
89     if (plugin_name.empty()) {
90       plugin_name = l10n_util::GetStringUTF16(IDS_UNKNOWN_PLUGIN_NAME);
91     } else if (kGTalkPluginName == plugin_name) {
92       UMA_HISTOGRAM_ENUMERATION("GTalkPlugin.Hung",
93           GetGTalkPluginVersion(plugin_version),
94           GTALK_PLUGIN_VERSION_MAX + 1);
95     }
96
97     if (logging::DialogsAreSuppressed()) {
98       NOTREACHED() << "Terminated a hung plugin process.";
99       *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
100     } else {
101       const base::string16 title = l10n_util::GetStringUTF16(
102           IDS_BROWSER_HANGMONITOR_TITLE);
103       const base::string16 message = l10n_util::GetStringFUTF16(
104           IDS_BROWSER_HANGMONITOR, plugin_name);
105       // Before displaying the message box, invoke SendMessageCallback on the
106       // hung window. If the callback ever hits, the window is not hung anymore
107       // and we can dismiss the message box.
108       SendMessageCallback(hung_window,
109                           WM_NULL,
110                           0,
111                           0,
112                           HungWindowResponseCallback,
113                           reinterpret_cast<ULONG_PTR>(this));
114       current_hung_plugin_window_ = hung_window;
115       if (chrome::ShowMessageBox(
116               NULL, title, message, chrome::MESSAGE_BOX_TYPE_QUESTION) ==
117           chrome::MESSAGE_BOX_RESULT_YES) {
118         *action = HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS;
119       } else {
120         // If the user choses to ignore the hung window warning, the
121         // message timeout for this window should be doubled. We only
122         // double the timeout property on the window if the property
123         // exists. The property is deleted if the window becomes
124         // responsive.
125         continue_hang_detection = false;
126 #pragma warning(disable:4311)
127         int child_window_message_timeout =
128             reinterpret_cast<int>(GetProp(
129                 hung_window, HungWindowDetector::kHungChildWindowTimeout));
130 #pragma warning(default:4311)
131         if (child_window_message_timeout) {
132           child_window_message_timeout *= 2;
133 #pragma warning(disable:4312)
134           SetProp(hung_window, HungWindowDetector::kHungChildWindowTimeout,
135                   reinterpret_cast<HANDLE>(child_window_message_timeout));
136 #pragma warning(default:4312)
137         }
138       }
139       current_hung_plugin_window_ = NULL;
140     }
141   }
142   if (HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS == *action) {
143     // Enable the top-level window just in case the plugin had been
144     // displaying a modal box that had disabled the top-level window
145     EnableWindow(top_level_window, TRUE);
146   }
147   return continue_hang_detection;
148 }
149
150 void HungPluginAction::OnWindowResponsive(HWND window) {
151   if (window == current_hung_plugin_window_) {
152     // The message timeout for this window should fallback to the default
153     // timeout as this window is now responsive.
154     RemoveProp(window, HungWindowDetector::kHungChildWindowTimeout);
155     // The monitored plugin recovered. Let's dismiss the message box.
156     EnumThreadWindows(GetCurrentThreadId(),
157                       reinterpret_cast<WNDENUMPROC>(DismissMessageBox),
158                       NULL);
159   }
160 }
161
162 // static
163 BOOL CALLBACK HungPluginAction::DismissMessageBox(HWND window, LPARAM ignore) {
164   base::string16 class_name = gfx::GetClassName(window);
165   // #32770 is the dialog window class which is the window class of
166   // the message box being displayed.
167   if (class_name == L"#32770") {
168     EndDialog(window, IDNO);
169     return FALSE;
170   }
171   return TRUE;
172 }
173
174 // static
175 void CALLBACK HungPluginAction::HungWindowResponseCallback(HWND target_window,
176                                                            UINT message,
177                                                            ULONG_PTR data,
178                                                            LRESULT result) {
179   HungPluginAction* instance = reinterpret_cast<HungPluginAction*>(data);
180   DCHECK(NULL != instance);
181   if (NULL != instance) {
182     instance->OnWindowResponsive(target_window);
183   }
184 }