platform_util_win.cc 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // Copyright (c) 2011 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 "chrome/browser/platform_util.h"
  5. #include <windows.h> // Must be in front of other Windows header files.
  6. #include <commdlg.h>
  7. #include <dwmapi.h>
  8. #include <shellapi.h>
  9. #include <shlobj.h>
  10. #include <stddef.h>
  11. #include <wrl/client.h>
  12. #include "base/base_paths.h"
  13. #include "base/bind.h"
  14. #include "base/files/file_path.h"
  15. #include "base/files/file_util.h"
  16. #include "base/logging.h"
  17. #include "base/path_service.h"
  18. #include "base/strings/string_util.h"
  19. #include "base/strings/utf_string_conversions.h"
  20. #include "base/task/thread_pool.h"
  21. #include "base/threading/scoped_blocking_call.h"
  22. #include "base/win/registry.h"
  23. #include "base/win/scoped_co_mem.h"
  24. #include "chrome/browser/platform_util_internal.h"
  25. #include "content/public/browser/browser_thread.h"
  26. #include "ui/base/win/shell.h"
  27. #include "ui/gfx/native_widget_types.h"
  28. #include "url/gurl.h"
  29. using content::BrowserThread;
  30. namespace platform_util {
  31. namespace {
  32. void ShowItemInFolderOnWorkerThread(const base::FilePath& full_path) {
  33. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  34. base::BlockingType::MAY_BLOCK);
  35. base::FilePath dir = full_path.DirName().AsEndingWithSeparator();
  36. // ParseDisplayName will fail if the directory is "C:", it must be "C:\\".
  37. if (dir.empty())
  38. return;
  39. Microsoft::WRL::ComPtr<IShellFolder> desktop;
  40. HRESULT hr = SHGetDesktopFolder(&desktop);
  41. if (FAILED(hr))
  42. return;
  43. base::win::ScopedCoMem<ITEMIDLIST> dir_item;
  44. hr = desktop->ParseDisplayName(NULL, NULL,
  45. const_cast<wchar_t *>(dir.value().c_str()),
  46. NULL, &dir_item, NULL);
  47. if (FAILED(hr))
  48. return;
  49. base::win::ScopedCoMem<ITEMIDLIST> file_item;
  50. hr = desktop->ParseDisplayName(NULL, NULL,
  51. const_cast<wchar_t *>(full_path.value().c_str()),
  52. NULL, &file_item, NULL);
  53. if (FAILED(hr))
  54. return;
  55. const ITEMIDLIST* highlight[] = {file_item};
  56. // Skip opening the folder during browser tests, to avoid leaving an open
  57. // file explorer window behind.
  58. if (!platform_util::internal::AreShellOperationsAllowed())
  59. return;
  60. hr = SHOpenFolderAndSelectItems(dir_item, std::size(highlight), highlight, 0);
  61. if (FAILED(hr)) {
  62. // On some systems, the above call mysteriously fails with "file not
  63. // found" even though the file is there. In these cases, ShellExecute()
  64. // seems to work as a fallback (although it won't select the file).
  65. if (hr == ERROR_FILE_NOT_FOUND) {
  66. ShellExecute(NULL, L"open", dir.value().c_str(), NULL, NULL, SW_SHOW);
  67. } else {
  68. LOG(WARNING) << " " << __func__ << "(): Can't open full_path = \""
  69. << full_path.value() << "\""
  70. << " hr = " << logging::SystemErrorCodeToString(hr);
  71. }
  72. }
  73. }
  74. void OpenExternalOnWorkerThread(const GURL& url) {
  75. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  76. base::BlockingType::MAY_BLOCK);
  77. // Quote the input scheme to be sure that the command does not have
  78. // parameters unexpected by the external program. This url should already
  79. // have been escaped.
  80. std::string escaped_url = url.spec();
  81. escaped_url.insert(0, "\"");
  82. escaped_url += "\"";
  83. // According to Mozilla in uriloader/exthandler/win/nsOSHelperAppService.cpp:
  84. // "Some versions of windows (Win2k before SP3, Win XP before SP1) crash in
  85. // ShellExecute on long URLs (bug 161357 on bugzilla.mozilla.org). IE 5 and 6
  86. // support URLS of 2083 chars in length, 2K is safe."
  87. //
  88. // It may be possible to increase this. https://crbug.com/727909
  89. const size_t kMaxUrlLength = 2048;
  90. if (escaped_url.length() > kMaxUrlLength)
  91. return;
  92. // Specify %windir%\system32 as the CWD so that any new proc spawned does not
  93. // inherit this proc's CWD. Without this, uninstalls may be broken by a
  94. // long-lived child proc that holds a handle to the browser's version
  95. // directory (the browser's CWD). A process's CWD is in the standard list of
  96. // directories to search when loading a DLL, and precedes the system directory
  97. // when safe DLL search mode is disabled (not the default). Setting the CWD to
  98. // the system directory is a nice way to mitigate a potential DLL search order
  99. // hijack for processes that don't implement their own mitigation.
  100. base::FilePath system_dir;
  101. base::PathService::Get(base::DIR_SYSTEM, &system_dir);
  102. if (reinterpret_cast<ULONG_PTR>(ShellExecuteA(
  103. NULL, "open", escaped_url.c_str(), NULL,
  104. system_dir.AsUTF8Unsafe().c_str(), SW_SHOWNORMAL)) <= 32) {
  105. // On failure, it may be good to display a message to the user.
  106. // https://crbug.com/727913
  107. return;
  108. }
  109. }
  110. } // namespace
  111. void ShowItemInFolder(Profile* profile, const base::FilePath& full_path) {
  112. base::ThreadPool::CreateCOMSTATaskRunner(
  113. {base::MayBlock(), base::TaskPriority::USER_BLOCKING})
  114. ->PostTask(FROM_HERE,
  115. base::BindOnce(&ShowItemInFolderOnWorkerThread, full_path));
  116. }
  117. namespace internal {
  118. void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type) {
  119. // May result in an interactive dialog.
  120. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  121. base::BlockingType::MAY_BLOCK);
  122. switch (type) {
  123. case OPEN_FILE:
  124. ui::win::OpenFileViaShell(path);
  125. break;
  126. case OPEN_FOLDER:
  127. ui::win::OpenFolderViaShell(path);
  128. break;
  129. }
  130. }
  131. } // namespace internal
  132. void OpenExternal(Profile* profile, const GURL& url) {
  133. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  134. base::ThreadPool::CreateCOMSTATaskRunner(
  135. {base::MayBlock(), base::TaskPriority::USER_BLOCKING})
  136. ->PostTask(FROM_HERE, base::BindOnce(&OpenExternalOnWorkerThread, url));
  137. }
  138. } // namespace platform_util