base_shell_dialog_win.cc 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  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. #include "ui/shell_dialogs/base_shell_dialog_win.h"
  5. #include <algorithm>
  6. #include "base/no_destructor.h"
  7. #include "base/task/thread_pool.h"
  8. #include "base/win/scoped_com_initializer.h"
  9. namespace ui {
  10. namespace {
  11. // Creates a SingleThreadTaskRunner to run a shell dialog on. Each dialog
  12. // requires its own dedicated single-threaded sequence otherwise in some
  13. // situations where a singleton owns a single instance of this object we can
  14. // have a situation where a modal dialog in one window blocks the appearance
  15. // of a modal dialog in another.
  16. scoped_refptr<base::SingleThreadTaskRunner> CreateDialogTaskRunner() {
  17. return base::ThreadPool::CreateCOMSTATaskRunner(
  18. {base::TaskPriority::USER_BLOCKING,
  19. base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, base::MayBlock()},
  20. base::SingleThreadTaskRunnerThreadMode::DEDICATED);
  21. }
  22. // Enables the window |owner|. Can only be run from the UI thread.
  23. void SetOwnerEnabled(HWND owner, bool enabled) {
  24. if (IsWindow(owner))
  25. EnableWindow(owner, enabled);
  26. }
  27. } // namespace
  28. BaseShellDialogImpl::RunState::RunState() = default;
  29. BaseShellDialogImpl::RunState::~RunState() = default;
  30. // static
  31. int BaseShellDialogImpl::instance_count_ = 0;
  32. BaseShellDialogImpl::BaseShellDialogImpl() {
  33. ++instance_count_;
  34. }
  35. BaseShellDialogImpl::~BaseShellDialogImpl() {
  36. // All runs should be complete by the time this is called!
  37. if (--instance_count_ == 0)
  38. DCHECK(GetOwners().empty());
  39. }
  40. // static
  41. BaseShellDialogImpl::Owners& BaseShellDialogImpl::GetOwners() {
  42. static base::NoDestructor<BaseShellDialogImpl::Owners> owners;
  43. return *owners;
  44. }
  45. // static
  46. void BaseShellDialogImpl::DisableOwner(HWND owner) {
  47. SetOwnerEnabled(owner, false);
  48. }
  49. std::unique_ptr<BaseShellDialogImpl::RunState> BaseShellDialogImpl::BeginRun(
  50. HWND owner) {
  51. // Cannot run a modal shell dialog if one is already running for this owner.
  52. DCHECK(!IsRunningDialogForOwner(owner));
  53. // The owner must be a top level window, otherwise we could end up with two
  54. // entries in our map for the same top level window.
  55. DCHECK(!owner || owner == GetAncestor(owner, GA_ROOT));
  56. auto run_state = std::make_unique<RunState>();
  57. run_state->dialog_task_runner = CreateDialogTaskRunner();
  58. run_state->owner = owner;
  59. if (owner) {
  60. GetOwners().insert(owner);
  61. DisableOwner(owner);
  62. }
  63. return run_state;
  64. }
  65. void BaseShellDialogImpl::EndRun(std::unique_ptr<RunState> run_state) {
  66. if (run_state->owner) {
  67. DCHECK(IsRunningDialogForOwner(run_state->owner));
  68. SetOwnerEnabled(run_state->owner, true);
  69. DCHECK(GetOwners().find(run_state->owner) != GetOwners().end());
  70. GetOwners().erase(run_state->owner);
  71. }
  72. }
  73. bool BaseShellDialogImpl::IsRunningDialogForOwner(HWND owner) const {
  74. return (owner && GetOwners().find(owner) != GetOwners().end());
  75. }
  76. } // namespace ui