Index: remoting/host/it2me/it2me_confirmation_dialog_win.cc |
diff --git a/remoting/host/it2me/it2me_confirmation_dialog_win.cc b/remoting/host/it2me/it2me_confirmation_dialog_win.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4e8453f91e571f866c2b1e68fb7e8ced9ad3c9bf |
--- /dev/null |
+++ b/remoting/host/it2me/it2me_confirmation_dialog_win.cc |
@@ -0,0 +1,216 @@ |
+// Copyright 2016 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 <commctrl.h> |
+ |
+#include <memory> |
+#include <string> |
+ |
+#include "base/bind.h" |
+#include "base/callback.h" |
+#include "base/callback_helpers.h" |
+#include "base/i18n/message_formatter.h" |
+#include "base/logging.h" |
+#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/strings/string_util.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "remoting/host/it2me/it2me_confirmation_dialog.h" |
+#include "remoting/host/win/core_resource.h" |
+ |
+namespace remoting { |
+ |
+namespace { |
+ |
+// Time to wait before closing the dialog and cancelling the connection. |
+const int kDialogTimeoutMs = 60 * 1000; |
+ |
+const HRESULT kTimeoutErrorCode = E_ABORT; |
+ |
+// Loads an embedded string resource from the specified module. |
+bool LoadStringResource(HMODULE resource_module, |
+ int resource_id, |
+ base::string16* string) { |
+ DCHECK(resource_module); |
+ DCHECK(string); |
+ |
+ string->clear(); |
+ |
+ const wchar_t* string_resource = nullptr; |
+ int string_length = LoadStringW(resource_module, resource_id, |
+ reinterpret_cast<wchar_t*>(&string_resource), |
+ /*nBufferMax=*/0); |
+ if (string_length <= 0) { |
+ PLOG(ERROR) << "LoadStringW() failed"; |
+ return false; |
+ } |
+ |
+ string->append(string_resource, string_length); |
+ return true; |
+} |
+ |
+class It2MeConfirmationDialogWin : public It2MeConfirmationDialog { |
+ public: |
+ It2MeConfirmationDialogWin(); |
+ ~It2MeConfirmationDialogWin() override; |
+ |
+ static HRESULT CALLBACK TaskDialogCallbackProc(HWND hwnd, |
+ UINT notification, |
+ WPARAM w_param, |
+ LPARAM l_param, |
+ LONG_PTR ref_data); |
+ |
+ // It2MeConfirmationDialog implementation. |
+ void Show(const std::string& remote_user_email, |
+ const ResultCallback& callback) override; |
+ |
+ private: |
+ // Tracks whether the dialog was in the foreground the last time we checked. |
+ // Default to true so we will attempt to bring it back if it starts in the |
+ // background for some reason. |
+ bool is_foreground_window_ = true; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(It2MeConfirmationDialogWin); |
+}; |
+ |
+It2MeConfirmationDialogWin::It2MeConfirmationDialogWin() {} |
+ |
+It2MeConfirmationDialogWin::~It2MeConfirmationDialogWin() {} |
+ |
+void It2MeConfirmationDialogWin::Show(const std::string& remote_user_email, |
+ const ResultCallback& callback) { |
+ DCHECK(!remote_user_email.empty()); |
+ DCHECK(!callback.is_null()); |
+ |
+ // Default to a cancelled state. We only accept the connection if the user |
+ // explicitly allows it. |
+ Result result = Result::CANCEL; |
+ |
+ // |resource_module| does not need to be freed as GetModuleHandle() does not |
+ // increment the refcount for the module. This DLL is not unloaded until the |
+ // process exits so using a stored handle is safe. |
+ HMODULE resource_module = GetModuleHandle(L"remoting_core.dll"); |
+ if (resource_module == nullptr) { |
+ PLOG(ERROR) << "GetModuleHandle() failed"; |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ base::string16 title_text; |
+ if (!LoadStringResource(resource_module, IDS_PRODUCT_NAME, &title_text)) { |
+ LOG(ERROR) << "Failed to load title text for confirmation dialog."; |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ base::string16 message_text; |
+ if (!LoadStringResource(resource_module, |
+ IDS_SHARE_CONFIRM_DIALOG_MESSAGE_WITH_USERNAME, |
+ &message_text)) { |
+ LOG(ERROR) << "Failed to load message text for confirmation dialog."; |
+ callback.Run(result); |
+ return; |
+ } |
+ message_text = base::i18n::MessageFormatter::FormatWithNumberedArgs( |
+ message_text, base::UTF8ToUTF16(remote_user_email)); |
+ |
+ base::string16 share_button_text; |
+ if (!LoadStringResource(resource_module, IDS_SHARE_CONFIRM_DIALOG_CONFIRM, |
+ &share_button_text)) { |
+ LOG(ERROR) << "Failed to load share button text for confirmation dialog."; |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ base::string16 decline_button_text; |
+ if (!LoadStringResource(resource_module, IDS_SHARE_CONFIRM_DIALOG_DECLINE, |
+ &decline_button_text)) { |
+ LOG(ERROR) << "Failed to load decline button text for confirmation dialog."; |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ TASKDIALOG_BUTTON dialog_buttons[] = { |
+ {IDYES, share_button_text.c_str()}, {IDNO, decline_button_text.c_str()}, |
+ }; |
+ |
+ TASKDIALOGCONFIG dialog_config = {0}; |
+ dialog_config.cbSize = sizeof(dialog_config); |
+ dialog_config.hInstance = resource_module; |
+ dialog_config.pszWindowTitle = title_text.c_str(); |
+ dialog_config.pszMainInstruction = message_text.c_str(); |
+ dialog_config.pszMainIcon = MAKEINTRESOURCE(IDI_CHROME_REMOTE_DESKTOP); |
+ dialog_config.dwFlags = TDF_CALLBACK_TIMER; |
+ dialog_config.pfCallback = &TaskDialogCallbackProc; |
+ dialog_config.lpCallbackData = reinterpret_cast<LONG_PTR>(this); |
+ dialog_config.cButtons = ARRAYSIZE(dialog_buttons); |
+ dialog_config.pButtons = dialog_buttons; |
+ dialog_config.nDefaultButton = IDNO; |
+ |
+ int button_result = 0; |
+ HRESULT hr = TaskDialogIndirect(&dialog_config, &button_result, |
+ /*pnRadioButton=*/nullptr, |
+ /*pfVerificationFlagChecked=*/nullptr); |
+ if (FAILED(hr)) { |
+ if (hr == kTimeoutErrorCode) { |
+ LOG(INFO) << "TaskDialog timed out."; |
+ } else { |
+ LOG(ERROR) << "TaskDialogIndirect() Failed: 0x" << std::hex << hr; |
+ } |
+ |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ if (button_result == IDYES) { |
+ // Only accept the connection if the user chose 'share'. |
+ result = Result::OK; |
+ } |
+ |
+ callback.Run(result); |
+} |
+ |
+HRESULT CALLBACK |
+It2MeConfirmationDialogWin::TaskDialogCallbackProc(HWND hwnd, |
+ UINT notification, |
+ WPARAM w_param, |
+ LPARAM l_param, |
+ LONG_PTR ref_data) { |
+ if (notification == TDN_TIMER) { |
+ if (w_param >= kDialogTimeoutMs) { |
+ // Close the dialog window if we have reached the timeout. |
+ return kTimeoutErrorCode; |
+ } |
+ |
+ // Ensure the window is visible before checking if it is in the foreground. |
+ if (!IsWindowVisible(hwnd)) { |
+ ShowWindow(hwnd, SW_SHOWNORMAL); |
+ } |
+ |
+ // Attempt to bring the dialog window to the foreground if needed. If the |
+ // window is in the background and cannot be brought forward, this call will |
+ // flash the placeholder on the taskbar. Do not call SetForegroundWindow() |
+ // multiple times as it will cause annoying flashing for the user. |
+ It2MeConfirmationDialogWin* dialog = |
+ reinterpret_cast<It2MeConfirmationDialogWin*>(ref_data); |
+ if (hwnd == GetForegroundWindow()) { |
+ dialog->is_foreground_window_ = true; |
+ } else if (dialog->is_foreground_window_) { |
+ SetForegroundWindow(hwnd); |
+ dialog->is_foreground_window_ = false; |
+ } |
+ } |
+ |
+ return S_OK; |
+} |
+ |
+} // namespace |
+ |
+std::unique_ptr<It2MeConfirmationDialog> |
+It2MeConfirmationDialogFactory::Create() { |
+ return base::MakeUnique<It2MeConfirmationDialogWin>(); |
+} |
+ |
+} // namespace remoting |