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