platform_util_win.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 5#include "chrome/browser/platform_util.h" 6 7#include <commdlg.h> 8#include <dwmapi.h> 9#include <shellapi.h> 10#include <shlobj.h> 11 12#include "base/bind.h" 13#include "base/bind_helpers.h" 14#include "base/files/file_path.h" 15#include "base/logging.h" 16#include "base/metrics/field_trial.h" 17#include "base/strings/string_util.h" 18#include "base/strings/utf_string_conversions.h" 19#include "base/win/registry.h" 20#include "base/win/scoped_co_mem.h" 21#include "base/win/scoped_comptr.h" 22#include "base/win/windows_version.h" 23#include "chrome/browser/lifetime/application_lifetime.h" 24#include "chrome/browser/ui/host_desktop.h" 25#include "chrome/common/chrome_utility_messages.h" 26#include "content/public/browser/browser_thread.h" 27#include "content/public/browser/utility_process_host.h" 28#include "ui/base/win/shell.h" 29#include "ui/gfx/native_widget_types.h" 30#include "url/gurl.h" 31 32using content::BrowserThread; 33 34namespace { 35 36void ShowItemInFolderOnFileThread(const base::FilePath& full_path) { 37 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 38 base::FilePath dir = full_path.DirName().AsEndingWithSeparator(); 39 // ParseDisplayName will fail if the directory is "C:", it must be "C:\\". 40 if (dir.empty()) 41 return; 42 43 typedef HRESULT (WINAPI *SHOpenFolderAndSelectItemsFuncPtr)( 44 PCIDLIST_ABSOLUTE pidl_Folder, 45 UINT cidl, 46 PCUITEMID_CHILD_ARRAY pidls, 47 DWORD flags); 48 49 static SHOpenFolderAndSelectItemsFuncPtr open_folder_and_select_itemsPtr = 50 NULL; 51 static bool initialize_open_folder_proc = true; 52 if (initialize_open_folder_proc) { 53 initialize_open_folder_proc = false; 54 // The SHOpenFolderAndSelectItems API is exposed by shell32 version 6 55 // and does not exist in Win2K. We attempt to retrieve this function export 56 // from shell32 and if it does not exist, we just invoke ShellExecute to 57 // open the folder thus losing the functionality to select the item in 58 // the process. 59 HMODULE shell32_base = GetModuleHandle(L"shell32.dll"); 60 if (!shell32_base) { 61 NOTREACHED() << " " << __FUNCTION__ << "(): Can't open shell32.dll"; 62 return; 63 } 64 open_folder_and_select_itemsPtr = 65 reinterpret_cast<SHOpenFolderAndSelectItemsFuncPtr> 66 (GetProcAddress(shell32_base, "SHOpenFolderAndSelectItems")); 67 } 68 if (!open_folder_and_select_itemsPtr) { 69 ShellExecute(NULL, L"open", dir.value().c_str(), NULL, NULL, SW_SHOW); 70 return; 71 } 72 73 base::win::ScopedComPtr<IShellFolder> desktop; 74 HRESULT hr = SHGetDesktopFolder(desktop.Receive()); 75 if (FAILED(hr)) 76 return; 77 78 base::win::ScopedCoMem<ITEMIDLIST> dir_item; 79 hr = desktop->ParseDisplayName(NULL, NULL, 80 const_cast<wchar_t *>(dir.value().c_str()), 81 NULL, &dir_item, NULL); 82 if (FAILED(hr)) 83 return; 84 85 base::win::ScopedCoMem<ITEMIDLIST> file_item; 86 hr = desktop->ParseDisplayName(NULL, NULL, 87 const_cast<wchar_t *>(full_path.value().c_str()), 88 NULL, &file_item, NULL); 89 if (FAILED(hr)) 90 return; 91 92 const ITEMIDLIST* highlight[] = { file_item }; 93 94 hr = (*open_folder_and_select_itemsPtr)(dir_item, arraysize(highlight), 95 highlight, NULL); 96 97 if (FAILED(hr)) { 98 // On some systems, the above call mysteriously fails with "file not 99 // found" even though the file is there. In these cases, ShellExecute() 100 // seems to work as a fallback (although it won't select the file). 101 if (hr == ERROR_FILE_NOT_FOUND) { 102 ShellExecute(NULL, L"open", dir.value().c_str(), NULL, NULL, SW_SHOW); 103 } else { 104 LOG(WARNING) << " " << __FUNCTION__ 105 << "(): Can't open full_path = \"" 106 << full_path.value() << "\"" 107 << " hr = " << logging::SystemErrorCodeToString(hr); 108 } 109 } 110} 111 112// Old ShellExecute crashes the process when the command for a given scheme 113// is empty. This function tells if it is. 114bool ValidateShellCommandForScheme(const std::string& scheme) { 115 base::win::RegKey key; 116 std::wstring registry_path = base::ASCIIToWide(scheme) + 117 L"\\shell\\open\\command"; 118 key.Open(HKEY_CLASSES_ROOT, registry_path.c_str(), KEY_READ); 119 if (!key.Valid()) 120 return false; 121 DWORD size = 0; 122 key.ReadValue(NULL, NULL, &size, NULL); 123 if (size <= 2) 124 return false; 125 return true; 126} 127 128void OpenExternalOnFileThread(const GURL& url) { 129 // Quote the input scheme to be sure that the command does not have 130 // parameters unexpected by the external program. This url should already 131 // have been escaped. 132 std::string escaped_url = url.spec(); 133 escaped_url.insert(0, "\""); 134 escaped_url += "\""; 135 136 // According to Mozilla in uriloader/exthandler/win/nsOSHelperAppService.cpp: 137 // "Some versions of windows (Win2k before SP3, Win XP before SP1) crash in 138 // ShellExecute on long URLs (bug 161357 on bugzilla.mozilla.org). IE 5 and 6 139 // support URLS of 2083 chars in length, 2K is safe." 140 const size_t kMaxUrlLength = 2048; 141 if (escaped_url.length() > kMaxUrlLength) { 142 NOTREACHED(); 143 return; 144 } 145 146 if (base::win::GetVersion() < base::win::VERSION_WIN7) { 147 if (!ValidateShellCommandForScheme(url.scheme())) 148 return; 149 } 150 151 if (reinterpret_cast<ULONG_PTR>(ShellExecuteA(NULL, "open", 152 escaped_url.c_str(), NULL, NULL, 153 SW_SHOWNORMAL)) <= 32) { 154 // We fail to execute the call. We could display a message to the user. 155 // TODO(nsylvain): we should also add a dialog to warn on errors. See 156 // bug 1136923. 157 return; 158 } 159} 160 161void OpenItemViaShellInUtilityProcess(const base::FilePath& full_path) { 162 base::WeakPtr<content::UtilityProcessHost> utility_process_host( 163 content::UtilityProcessHost::Create(NULL, NULL)->AsWeakPtr()); 164 utility_process_host->DisableSandbox(); 165 utility_process_host->Send(new ChromeUtilityMsg_OpenItemViaShell(full_path)); 166} 167 168} // namespace 169 170namespace platform_util { 171 172void ShowItemInFolder(Profile* profile, const base::FilePath& full_path) { 173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 174 175 if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH) 176 chrome::ActivateDesktopHelper(chrome::ASH_KEEP_RUNNING); 177 178 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 179 base::Bind(&ShowItemInFolderOnFileThread, full_path)); 180} 181 182void OpenItem(Profile* profile, const base::FilePath& full_path) { 183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 184 185 if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH) 186 chrome::ActivateDesktopHelper(chrome::ASH_KEEP_RUNNING); 187 188 if (base::FieldTrialList::FindFullName("IsolateShellOperations") == 189 "Enabled") { 190 BrowserThread::PostTask( 191 BrowserThread::IO, 192 FROM_HERE, 193 base::Bind(&OpenItemViaShellInUtilityProcess, full_path)); 194 } else { 195 BrowserThread::PostTask( 196 BrowserThread::FILE, 197 FROM_HERE, 198 base::Bind(base::IgnoreResult(&ui::win::OpenItemViaShell), full_path)); 199 } 200} 201 202void OpenExternal(Profile* profile, const GURL& url) { 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 204 205 if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH && 206 !url.SchemeIsHTTPOrHTTPS()) 207 chrome::ActivateDesktopHelper(chrome::ASH_KEEP_RUNNING); 208 209 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 210 base::Bind(&OpenExternalOnFileThread, url)); 211} 212 213#if !defined(USE_AURA) 214gfx::NativeWindow GetTopLevel(gfx::NativeView view) { 215 return ::GetAncestor(view, GA_ROOT); 216} 217 218gfx::NativeView GetParent(gfx::NativeView view) { 219 return ::GetParent(view); 220} 221 222bool IsWindowActive(gfx::NativeWindow window) { 223 return ::GetForegroundWindow() == window; 224} 225 226void ActivateWindow(gfx::NativeWindow window) { 227 ::SetForegroundWindow(window); 228} 229 230bool IsVisible(gfx::NativeView view) { 231 // MSVC complains if we don't include != 0. 232 return ::IsWindowVisible(view) != 0; 233} 234#endif 235 236} // namespace platform_util 237