Update code documentation for enum in EWK headers
[platform/framework/web/chromium-efl.git] / chrome / notification_helper / notification_activator.cc
1 // Copyright 2018 The Chromium Authors
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 "chrome/notification_helper/notification_activator.h"
6
7 #include <windows.h>
8
9 #include <shellapi.h>
10
11 #include <string>
12
13 #include "base/command_line.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/process/process.h"
16 #include "base/win/windows_types.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/notification_helper/notification_helper_util.h"
19 #include "chrome/notification_helper/trace_util.h"
20
21 namespace {
22
23 // The response entered by the user while interacting with the toast.
24 const wchar_t kUserResponse[] = L"userResponse";
25
26 // These values are persisted to logs. Entries should not be renumbered and
27 // numeric values should never be reused.
28 enum class NotificationActivatorPrimaryStatus {
29   kSuccess = 0,
30   kChromeExeMissing = 1,
31   kShellExecuteFailed = 2,
32   kMaxValue = kShellExecuteFailed,
33 };
34
35 // These values are persisted to logs. Entries should not be renumbered and
36 // numeric values should never be reused.
37 enum class NotificationActivatorSecondaryStatus {
38   kSuccess = 0,
39   kLaunchIdEmpty = 1 << 0,
40   kAllowSetForegroundWindowFailed = 1 << 1,
41   kProcessHandleMissing = 1 << 2,
42   kScenarioCount = 1 << 3,
43   kMaxValue = kScenarioCount,
44 };
45
46 void LogNotificationActivatorPrimaryStatus(
47     NotificationActivatorPrimaryStatus status) {
48   UMA_HISTOGRAM_ENUMERATION(
49       "Notifications.NotificationHelper.NotificationActivatorPrimaryStatus",
50       status);
51 }
52
53 void LogNotificationActivatorSecondaryStatus(
54     NotificationActivatorSecondaryStatus status) {
55   UMA_HISTOGRAM_ENUMERATION(
56       "Notifications.NotificationHelper.NotificationActivatorSecondaryStatus",
57       status);
58 }
59
60 }  // namespace
61
62 namespace notification_helper {
63
64 NotificationActivator::~NotificationActivator() = default;
65
66 // Handles toast activation outside of the browser process lifecycle by
67 // launching chrome.exe with --notification-launch-id. This new process may
68 // rendezvous to an existing browser process or become a new one, as
69 // appropriate.
70 //
71 // When this method is called, there are three possibilities depending on the
72 // running state of Chrome.
73 // 1) NOT_RUNNING: Chrome is not running.
74 // 2) NEW_INSTANCE: Chrome is running, but it's NOT the same instance that sent
75 //    the toast.
76 // 3) SAME_INSTANCE : Chrome is running, and it _is_ the same instance that sent
77 //    the toast.
78 //
79 // Chrome could attach an activation event handler to the toast so that Windows
80 // can call it directly to handle the activation. However, Windows makes this
81 // function call only in case SAME_INSTANCE. For the other two cases, Chrome
82 // needs to handle the activation on its own. Since there is no way to
83 // differentiate cases SAME_INSTANCE and NEW_INSTANCE in this
84 // notification_helper process, Chrome doesn't attach an activation event
85 // handler to the toast and handles all three cases through the command line.
86 HRESULT NotificationActivator::Activate(
87     LPCWSTR app_user_model_id,
88     LPCWSTR invoked_args,
89     const NOTIFICATION_USER_INPUT_DATA* data,
90     ULONG count) {
91   base::FilePath chrome_exe_path = GetChromeExePath();
92   if (chrome_exe_path.empty()) {
93     Trace(L"Failed to get chrome exe path\n");
94     LogNotificationActivatorPrimaryStatus(
95         NotificationActivatorPrimaryStatus::kChromeExeMissing);
96     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
97   }
98
99   int secondary_status =
100       static_cast<int>(NotificationActivatorSecondaryStatus::kSuccess);
101
102   // |invoked_args| contains the launch ID string encoded by Chrome. Chrome adds
103   // it to the launch argument of the toast and gets it back via |invoked_args|.
104   // Chrome needs the data to be able to look up the notification on its end.
105   //
106   // When the user clicks the Chrome app title rather than the notifications in
107   // the Action Center, an empty launch id string is generated. It is preferable
108   // to launch Chrome with this empty launch id in this scenario, which results
109   // in displaying a NTP.
110   if (invoked_args == nullptr || invoked_args[0] == 0) {
111     secondary_status |=
112         static_cast<int>(NotificationActivatorSecondaryStatus::kLaunchIdEmpty);
113   }
114   base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
115   command_line.AppendSwitchNative(switches::kNotificationLaunchId,
116                                   invoked_args);
117
118   // Check to see if a user response (inline reply) is also supplied.
119   for (ULONG i = 0; i < count; ++i) {
120     if (lstrcmpW(kUserResponse, data[i].Key) == 0) {
121       command_line.AppendSwitchNative(switches::kNotificationInlineReply,
122                                       data[i].Value);
123       break;
124     }
125   }
126
127   std::wstring params(command_line.GetCommandLineString());
128
129   SHELLEXECUTEINFO info;
130   memset(&info, 0, sizeof(info));
131   info.cbSize = sizeof(info);
132   info.fMask =
133       SEE_MASK_NOASYNC | SEE_MASK_FLAG_LOG_USAGE | SEE_MASK_NOCLOSEPROCESS;
134   info.lpFile = chrome_exe_path.value().c_str();
135   info.lpParameters = params.c_str();
136   info.nShow = SW_SHOWNORMAL;
137
138   if (!::ShellExecuteEx(&info)) {
139     DWORD error_code = ::GetLastError();
140     Trace(L"Unable to launch Chrome.exe; error: 0x%08X\n", error_code);
141     LogNotificationActivatorPrimaryStatus(
142         NotificationActivatorPrimaryStatus::kShellExecuteFailed);
143     return HRESULT_FROM_WIN32(error_code);
144   }
145
146   if (info.hProcess != nullptr) {
147     base::Process process(info.hProcess);
148     DWORD pid = ::GetProcessId(process.Handle());
149
150     // Despite the fact that the Windows notification center grants the helper
151     // permission to set the foreground window, the helper fails to pass the
152     // baton to Chrome at an alarming rate; see https://crbug.com/837796.
153     // Sending generic down/up key events seems to fix it.
154     INPUT keyboard_inputs[2] = {};
155
156     keyboard_inputs[0].type = INPUT_KEYBOARD;
157     keyboard_inputs[0].ki.dwFlags = 0;  // Key press.
158
159     keyboard_inputs[1] = keyboard_inputs[0];
160     keyboard_inputs[1].ki.dwFlags |= KEYEVENTF_KEYUP;  // key release.
161
162     ::SendInput(2, keyboard_inputs, sizeof(keyboard_inputs[0]));
163
164     if (!::AllowSetForegroundWindow(pid)) {
165 #if !defined(NDEBUG)
166       DWORD error_code = ::GetLastError();
167       Trace(L"Unable to forward activation privilege; error: 0x%08X\n",
168             error_code);
169 #endif
170       // The lack of ability to set the window to foreground is not reason
171       // enough to fail the activation call. The user will see the Chrome icon
172       // flash in the task bar if this happens, which is a graceful failure.
173       secondary_status |=
174           static_cast<int>(NotificationActivatorSecondaryStatus::
175                                kAllowSetForegroundWindowFailed);
176     }
177   } else {
178     secondary_status |= static_cast<int>(
179         NotificationActivatorSecondaryStatus::kProcessHandleMissing);
180   }
181
182   LogNotificationActivatorPrimaryStatus(
183       NotificationActivatorPrimaryStatus::kSuccess);
184
185   LogNotificationActivatorSecondaryStatus(
186       static_cast<NotificationActivatorSecondaryStatus>(secondary_status));
187
188   return S_OK;
189 }
190
191 }  // namespace notification_helper