Home | History | Annotate | Download | only in delegate_execute
      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 // Implementation of the CommandExecuteImpl class which implements the
      5 // IExecuteCommand and related interfaces for handling ShellExecute based
      6 // launches of the Chrome browser.
      7 
      8 #include "win8/delegate_execute/command_execute_impl.h"
      9 
     10 #include <shlguid.h>
     11 
     12 #include "base/file_util.h"
     13 #include "base/path_service.h"
     14 #include "base/process/launch.h"
     15 #include "base/process/process_handle.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "base/win/message_window.h"
     18 #include "base/win/registry.h"
     19 #include "base/win/scoped_co_mem.h"
     20 #include "base/win/scoped_handle.h"
     21 #include "base/win/scoped_process_information.h"
     22 #include "base/win/win_util.h"
     23 #include "chrome/common/chrome_constants.h"
     24 #include "chrome/common/chrome_paths.h"
     25 #include "chrome/common/chrome_switches.h"
     26 #include "chrome/installer/util/browser_distribution.h"
     27 #include "chrome/installer/util/install_util.h"
     28 #include "chrome/installer/util/shell_util.h"
     29 #include "chrome/installer/util/util_constants.h"
     30 #include "ui/base/clipboard/clipboard_util_win.h"
     31 #include "win8/delegate_execute/chrome_util.h"
     32 #include "win8/delegate_execute/delegate_execute_util.h"
     33 #include "win8/viewer/metro_viewer_constants.h"
     34 
     35 namespace {
     36 
     37 // Helper function to retrieve the url from IShellItem interface passed in.
     38 // Returns S_OK on success.
     39 HRESULT GetUrlFromShellItem(IShellItem* shell_item, string16* url) {
     40   DCHECK(shell_item);
     41   DCHECK(url);
     42   // First attempt to get the url from the underlying IDataObject if any. This
     43   // ensures that we get the full url, i.e. including the anchor.
     44   // If we fail to get the underlying IDataObject we retrieve the url via the
     45   // IShellItem::GetDisplayName function.
     46   CComPtr<IDataObject> object;
     47   HRESULT hr = shell_item->BindToHandler(NULL,
     48                                          BHID_DataObject,
     49                                          IID_IDataObject,
     50                                          reinterpret_cast<void**>(&object));
     51   if (SUCCEEDED(hr)) {
     52     DCHECK(object);
     53     if (ui::ClipboardUtil::GetPlainText(object, url))
     54       return S_OK;
     55   }
     56 
     57   base::win::ScopedCoMem<wchar_t> name;
     58   hr = shell_item->GetDisplayName(SIGDN_URL, &name);
     59   if (hr != S_OK) {
     60     AtlTrace("Failed to get display name\n");
     61     return hr;
     62   }
     63 
     64   *url = static_cast<const wchar_t*>(name);
     65   AtlTrace("Retrieved url from display name %ls\n", url->c_str());
     66   return S_OK;
     67 }
     68 
     69 #if defined(USE_AURA)
     70 bool LaunchChromeBrowserProcess() {
     71   base::FilePath delegate_exe_path;
     72   if (!PathService::Get(base::FILE_EXE, &delegate_exe_path))
     73     return false;
     74 
     75   // First try and go up a level to find chrome.exe.
     76   base::FilePath chrome_exe_path =
     77       delegate_exe_path.DirName()
     78                        .DirName()
     79                        .Append(chrome::kBrowserProcessExecutableName);
     80   if (!base::PathExists(chrome_exe_path)) {
     81     // Try looking in the current directory if we couldn't find it one up in
     82     // order to support developer installs.
     83     chrome_exe_path =
     84         delegate_exe_path.DirName()
     85                          .Append(chrome::kBrowserProcessExecutableName);
     86   }
     87 
     88   if (!base::PathExists(chrome_exe_path)) {
     89     AtlTrace("Could not locate chrome.exe at: %ls\n",
     90              chrome_exe_path.value().c_str());
     91     return false;
     92   }
     93 
     94   CommandLine cl(chrome_exe_path);
     95 
     96   // Prevent a Chrome window from showing up on the desktop.
     97   cl.AppendSwitch(switches::kSilentLaunch);
     98 
     99   // Tell Chrome to connect to the Metro viewer process.
    100   cl.AppendSwitch(switches::kViewerConnect);
    101 
    102   base::LaunchOptions launch_options;
    103   launch_options.start_hidden = true;
    104 
    105   return base::LaunchProcess(cl, launch_options, NULL);
    106 }
    107 #endif  // defined(USE_AURA)
    108 
    109 }  // namespace
    110 
    111 bool CommandExecuteImpl::path_provider_initialized_ = false;
    112 
    113 // CommandExecuteImpl is resposible for activating chrome in Windows 8. The
    114 // flow is complicated and this tries to highlight the important events.
    115 // The current approach is to have a single instance of chrome either
    116 // running in desktop or metro mode. If there is no current instance then
    117 // the desktop shortcut launches desktop chrome and the metro tile or search
    118 // charm launches metro chrome.
    119 // If chrome is running then focus/activation is given to the existing one
    120 // regarless of what launch point the user used.
    121 //
    122 // The general flow when chrome is the default browser is as follows:
    123 //
    124 // 1- User interacts with launch point (icon, tile, search, shellexec, etc)
    125 // 2- Windows finds the appid for launch item and resolves it to chrome
    126 // 3- Windows activates CommandExecuteImpl inside a surrogate process
    127 // 4- Windows calls the following sequence of entry points:
    128 //    CommandExecuteImpl::SetShowWindow
    129 //    CommandExecuteImpl::SetPosition
    130 //    CommandExecuteImpl::SetDirectory
    131 //    CommandExecuteImpl::SetParameter
    132 //    CommandExecuteImpl::SetNoShowUI
    133 //    CommandExecuteImpl::SetSelection
    134 //    CommandExecuteImpl::Initialize
    135 //    Up to this point the code basically just gathers values passed in, like
    136 //    the launch scheme (or url) and the activation verb.
    137 // 5- Windows calls CommandExecuteImpl::Getvalue()
    138 //    Here we need to return AHE_IMMERSIVE or AHE_DESKTOP. That depends on:
    139 //    a) if run in high-integrity return AHE_DESKTOP
    140 //    b) if chrome is running return the AHE_ mode of chrome
    141 //    c) else we return what GetLaunchMode() tells us, which is:
    142 //       i) if the command line --force-xxx is present return that
    143 //       ii) if the registry 'launch_mode' exists return that
    144 //       iii) if IsTouchEnabledDevice() is true return AHE_IMMERSIVE
    145 //       iv) else return AHE_DESKTOP
    146 // 6- If we returned AHE_IMMERSIVE in step 5 windows might not call us back
    147 //    and simply activate chrome in metro by itself, however in some cases
    148 //    it might proceed at step 7.
    149 //    As far as we know if we return AHE_DESKTOP then step 7 always happens.
    150 // 7- Windows calls CommandExecuteImpl::Execute()
    151 //    Here we call GetLaunchMode() which returns the cached answer
    152 //    computed at step 5c. which can be:
    153 //    a) ECHUIM_DESKTOP then we call LaunchDesktopChrome() that calls
    154 //       ::CreateProcess and we exit at this point even on failure.
    155 //    b) else we call one of the IApplicationActivationManager activation
    156 //       functions depending on the parameters passed in step 4.
    157 //    c) If the activation returns E_APPLICATION_NOT_REGISTERED, then we fall
    158 //       back to launching chrome on the desktop via LaunchDestopChrome().
    159 //
    160 // Note that if a command line --force-xxx is present we write that launch mode
    161 // in the registry so next time the logic reaches 5c-ii it will use the same
    162 // mode again.
    163 //
    164 // Also note that if we are not the default browser and IsTouchEnabledDevice()
    165 // returns true, launching chrome can go all the way to 7c, which might be
    166 // a slow way to start chrome.
    167 //
    168 CommandExecuteImpl::CommandExecuteImpl()
    169     : parameters_(CommandLine::NO_PROGRAM),
    170       launch_scheme_(INTERNET_SCHEME_DEFAULT),
    171       integrity_level_(base::INTEGRITY_UNKNOWN),
    172       chrome_mode_(ECHUIM_SYSTEM_LAUNCHER) {
    173   memset(&start_info_, 0, sizeof(start_info_));
    174   start_info_.cb = sizeof(start_info_);
    175 
    176   // We need to query the user data dir of chrome so we need chrome's
    177   // path provider. We can be created multiplie times in a single instance
    178   // however so make sure we do this only once.
    179   if (!path_provider_initialized_) {
    180     chrome::RegisterPathProvider();
    181     path_provider_initialized_ = true;
    182   }
    183 }
    184 
    185 // CommandExecuteImpl
    186 STDMETHODIMP CommandExecuteImpl::SetKeyState(DWORD key_state) {
    187   AtlTrace("In %hs\n", __FUNCTION__);
    188   return S_OK;
    189 }
    190 
    191 STDMETHODIMP CommandExecuteImpl::SetParameters(LPCWSTR params) {
    192   AtlTrace("In %hs [%S]\n", __FUNCTION__, params);
    193   parameters_ = delegate_execute::CommandLineFromParameters(params);
    194   return S_OK;
    195 }
    196 
    197 STDMETHODIMP CommandExecuteImpl::SetPosition(POINT pt) {
    198   AtlTrace("In %hs\n", __FUNCTION__);
    199   return S_OK;
    200 }
    201 
    202 STDMETHODIMP CommandExecuteImpl::SetShowWindow(int show) {
    203   AtlTrace("In %hs  show=%d\n", __FUNCTION__, show);
    204   start_info_.wShowWindow = show;
    205   start_info_.dwFlags |= STARTF_USESHOWWINDOW;
    206   return S_OK;
    207 }
    208 
    209 STDMETHODIMP CommandExecuteImpl::SetNoShowUI(BOOL no_show_ui) {
    210   AtlTrace("In %hs no_show=%d\n", __FUNCTION__, no_show_ui);
    211   return S_OK;
    212 }
    213 
    214 STDMETHODIMP CommandExecuteImpl::SetDirectory(LPCWSTR directory) {
    215   AtlTrace("In %hs\n", __FUNCTION__);
    216   return S_OK;
    217 }
    218 
    219 STDMETHODIMP CommandExecuteImpl::GetValue(enum AHE_TYPE* pahe) {
    220   AtlTrace("In %hs\n", __FUNCTION__);
    221 
    222   if (!GetLaunchScheme(&display_name_, &launch_scheme_)) {
    223     AtlTrace("Failed to get scheme, E_FAIL\n");
    224     return E_FAIL;
    225   }
    226 
    227   if (integrity_level_ == base::HIGH_INTEGRITY) {
    228     // Metro mode apps don't work in high integrity mode.
    229     AtlTrace("High integrity, AHE_DESKTOP\n");
    230     *pahe = AHE_DESKTOP;
    231     return S_OK;
    232   }
    233 
    234   if (GetAsyncKeyState(VK_SHIFT) && GetAsyncKeyState(VK_F11)) {
    235     AtlTrace("Using Shift-F11 debug hook, returning AHE_IMMERSIVE\n");
    236     *pahe = AHE_IMMERSIVE;
    237 
    238 #if defined(USE_AURA)
    239     // Launch the chrome browser process that metro chrome will connect to.
    240     LaunchChromeBrowserProcess();
    241 #endif
    242 
    243     return S_OK;
    244   }
    245 
    246   if (GetAsyncKeyState(VK_SHIFT) && GetAsyncKeyState(VK_F12)) {
    247     AtlTrace("Using Shift-F12 debug hook, returning AHE_DESKTOP\n");
    248     *pahe = AHE_DESKTOP;
    249     return S_OK;
    250   }
    251 
    252   base::FilePath user_data_dir;
    253   if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))  {
    254     AtlTrace("Failed to get chrome's data dir path, E_FAIL\n");
    255     return E_FAIL;
    256   }
    257 
    258   bool decision_made = false;
    259 
    260   // New Aura/Ash world we don't want to go throgh FindWindow path
    261   // and instead take decision based on launch mode.
    262 #if !defined(USE_AURA)
    263   HWND chrome_window = base::win::MessageWindow::FindWindow(
    264       user_data_dir.value());
    265   if (chrome_window) {
    266     AtlTrace("Found chrome window %p\n", chrome_window);
    267     // The failure cases below are deemed to happen due to the inherently racy
    268     // procedure of going from chrome's window to process handle during which
    269     // chrome might have exited. Failing here would probably just cause the
    270     // user to retry at which point we would do the right thing.
    271     DWORD chrome_pid = 0;
    272     ::GetWindowThreadProcessId(chrome_window, &chrome_pid);
    273     if (!chrome_pid) {
    274       AtlTrace("Failed to get chrome's PID, E_FAIL\n");
    275       return E_FAIL;
    276     }
    277     base::win::ScopedHandle process(
    278         ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, chrome_pid));
    279     if (!process.IsValid()) {
    280       AtlTrace("Failed to open chrome's process [%d], E_FAIL\n", chrome_pid);
    281       return E_FAIL;
    282     }
    283 
    284     if (IsImmersiveProcess(process.Get())) {
    285       AtlTrace("Chrome [%d] is inmmersive, AHE_IMMERSIVE\n", chrome_pid);
    286       chrome_mode_ = ECHUIM_IMMERSIVE;
    287       *pahe = AHE_IMMERSIVE;
    288     } else {
    289       AtlTrace("Chrome [%d] is Desktop, AHE_DESKTOP\n");
    290       chrome_mode_ = ECHUIM_DESKTOP;
    291       *pahe = AHE_DESKTOP;
    292     }
    293 
    294     decision_made = true;
    295   }
    296 #endif
    297 
    298   if (!decision_made) {
    299     EC_HOST_UI_MODE mode = GetLaunchMode();
    300     *pahe = (mode == ECHUIM_DESKTOP) ? AHE_DESKTOP : AHE_IMMERSIVE;
    301   }
    302 
    303 #if defined(USE_AURA)
    304   if (*pahe == AHE_IMMERSIVE && verb_ != win8::kMetroViewerConnectVerb)
    305     LaunchChromeBrowserProcess();
    306 #endif
    307 
    308   return S_OK;
    309 }
    310 
    311 STDMETHODIMP CommandExecuteImpl::Execute() {
    312   AtlTrace("In %hs\n", __FUNCTION__);
    313 
    314   if (integrity_level_ == base::HIGH_INTEGRITY)
    315     return LaunchDesktopChrome();
    316 
    317   EC_HOST_UI_MODE mode = GetLaunchMode();
    318   if (mode == ECHUIM_DESKTOP)
    319     return LaunchDesktopChrome();
    320 
    321   HRESULT hr = E_FAIL;
    322   CComPtr<IApplicationActivationManager> activation_manager;
    323   hr = activation_manager.CoCreateInstance(CLSID_ApplicationActivationManager);
    324   if (!activation_manager) {
    325     AtlTrace("Failed to get the activation manager, error 0x%x\n", hr);
    326     return S_OK;
    327   }
    328 
    329   BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
    330   bool is_per_user_install = InstallUtil::IsPerUserInstall(
    331       chrome_exe_.value().c_str());
    332   string16 app_id = ShellUtil::GetBrowserModelId(
    333       distribution, is_per_user_install);
    334 
    335   DWORD pid = 0;
    336   if (launch_scheme_ == INTERNET_SCHEME_FILE &&
    337       display_name_.find(installer::kChromeExe) != string16::npos) {
    338     AtlTrace("Activating for file\n");
    339     hr = activation_manager->ActivateApplication(app_id.c_str(),
    340                                                  verb_.c_str(),
    341                                                  AO_NONE,
    342                                                  &pid);
    343   } else {
    344     AtlTrace("Activating for protocol\n");
    345     hr = activation_manager->ActivateForProtocol(app_id.c_str(),
    346                                                  item_array_,
    347                                                  &pid);
    348   }
    349   if (hr == E_APPLICATION_NOT_REGISTERED) {
    350     AtlTrace("Metro chrome is not registered, launching in desktop\n");
    351     return LaunchDesktopChrome();
    352   }
    353   AtlTrace("Metro Chrome launch, pid=%d, returned 0x%x\n", pid, hr);
    354   return S_OK;
    355 }
    356 
    357 STDMETHODIMP CommandExecuteImpl::Initialize(LPCWSTR name,
    358                                             IPropertyBag* bag) {
    359   AtlTrace("In %hs\n", __FUNCTION__);
    360   if (!FindChromeExe(&chrome_exe_))
    361     return E_FAIL;
    362   delegate_execute::UpdateChromeIfNeeded(chrome_exe_);
    363   if (name) {
    364     AtlTrace("Verb is %S\n", name);
    365     verb_ = name;
    366   }
    367 
    368   base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(),
    369                                  &integrity_level_);
    370   if (integrity_level_ == base::HIGH_INTEGRITY) {
    371     AtlTrace("Delegate execute launched in high integrity level\n");
    372   }
    373   return S_OK;
    374 }
    375 
    376 STDMETHODIMP CommandExecuteImpl::SetSelection(IShellItemArray* item_array) {
    377   AtlTrace("In %hs\n", __FUNCTION__);
    378   item_array_ = item_array;
    379   return S_OK;
    380 }
    381 
    382 STDMETHODIMP CommandExecuteImpl::GetSelection(REFIID riid, void** selection) {
    383   AtlTrace("In %hs\n", __FUNCTION__);
    384   return S_OK;
    385 }
    386 
    387 STDMETHODIMP CommandExecuteImpl::AllowForegroundTransfer(void* reserved) {
    388   AtlTrace("In %hs\n", __FUNCTION__);
    389   return S_OK;
    390 }
    391 
    392 // Returns false if chrome.exe cannot be found.
    393 // static
    394 bool CommandExecuteImpl::FindChromeExe(base::FilePath* chrome_exe) {
    395   AtlTrace("In %hs\n", __FUNCTION__);
    396   // Look for chrome.exe one folder above delegate_execute.exe (as expected in
    397   // Chrome installs). Failing that, look for it alonside delegate_execute.exe.
    398   base::FilePath dir_exe;
    399   if (!PathService::Get(base::DIR_EXE, &dir_exe)) {
    400     AtlTrace("Failed to get current exe path\n");
    401     return false;
    402   }
    403 
    404   *chrome_exe = dir_exe.DirName().Append(chrome::kBrowserProcessExecutableName);
    405   if (!base::PathExists(*chrome_exe)) {
    406     *chrome_exe = dir_exe.Append(chrome::kBrowserProcessExecutableName);
    407     if (!base::PathExists(*chrome_exe)) {
    408       AtlTrace("Failed to find chrome exe file\n");
    409       return false;
    410     }
    411   }
    412 
    413   AtlTrace("Got chrome exe path as %ls\n", chrome_exe->value().c_str());
    414   return true;
    415 }
    416 
    417 bool CommandExecuteImpl::GetLaunchScheme(
    418     string16* display_name, INTERNET_SCHEME* scheme) {
    419   if (!item_array_)
    420     return false;
    421 
    422   ATLASSERT(display_name);
    423   ATLASSERT(scheme);
    424 
    425   DWORD count = 0;
    426   item_array_->GetCount(&count);
    427 
    428   if (count != 1) {
    429     AtlTrace("Cannot handle %d elements in the IShellItemArray\n", count);
    430     return false;
    431   }
    432 
    433   CComPtr<IEnumShellItems> items;
    434   item_array_->EnumItems(&items);
    435   CComPtr<IShellItem> shell_item;
    436   HRESULT hr = items->Next(1, &shell_item, &count);
    437   if (hr != S_OK) {
    438     AtlTrace("Failed to read element from the IShellItemsArray\n");
    439     return false;
    440   }
    441 
    442   hr = GetUrlFromShellItem(shell_item, display_name);
    443   if (FAILED(hr)) {
    444     AtlTrace("Failed to get url. Error 0x%x\n", hr);
    445     return false;
    446   }
    447 
    448   AtlTrace("url [%ls]\n", display_name->c_str());
    449 
    450   wchar_t scheme_name[16];
    451   URL_COMPONENTS components = {0};
    452   components.lpszScheme = scheme_name;
    453   components.dwSchemeLength = sizeof(scheme_name)/sizeof(scheme_name[0]);
    454 
    455   components.dwStructSize = sizeof(components);
    456   if (!InternetCrackUrlW(display_name->c_str(), 0, 0, &components)) {
    457     AtlTrace("Failed to crack url %ls\n", display_name->c_str());
    458     return false;
    459   }
    460 
    461   AtlTrace("Launch scheme is [%ls] (%d)\n", scheme_name, components.nScheme);
    462   *scheme = components.nScheme;
    463   return true;
    464 }
    465 
    466 HRESULT CommandExecuteImpl::LaunchDesktopChrome() {
    467   AtlTrace("In %hs\n", __FUNCTION__);
    468   string16 display_name = display_name_;
    469 
    470   switch (launch_scheme_) {
    471     case INTERNET_SCHEME_FILE:
    472       // If anything other than chrome.exe is passed in the display name we
    473       // should honor it. For e.g. If the user clicks on a html file when
    474       // chrome is the default we should treat it as a parameter to be passed
    475       // to chrome.
    476       if (display_name.find(installer::kChromeExe) != string16::npos)
    477         display_name.clear();
    478       break;
    479 
    480     default:
    481       break;
    482   }
    483 
    484   CommandLine chrome(
    485       delegate_execute::MakeChromeCommandLine(chrome_exe_, parameters_,
    486                                               display_name));
    487   string16 command_line(chrome.GetCommandLineString());
    488 
    489   AtlTrace("Formatted command line is %ls\n", command_line.c_str());
    490 
    491   PROCESS_INFORMATION temp_process_info = {};
    492   BOOL ret = CreateProcess(chrome_exe_.value().c_str(),
    493                            const_cast<LPWSTR>(command_line.c_str()),
    494                            NULL, NULL, FALSE, 0, NULL, NULL, &start_info_,
    495                            &temp_process_info);
    496   if (ret) {
    497     base::win::ScopedProcessInformation proc_info(temp_process_info);
    498     AtlTrace("Process id is %d\n", proc_info.process_id());
    499     AllowSetForegroundWindow(proc_info.process_id());
    500   } else {
    501     AtlTrace("Process launch failed, error %d\n", ::GetLastError());
    502   }
    503 
    504   return S_OK;
    505 }
    506 
    507 EC_HOST_UI_MODE CommandExecuteImpl::GetLaunchMode() {
    508   // See the header file for an explanation of the mode selection logic.
    509   static bool launch_mode_determined = false;
    510   static EC_HOST_UI_MODE launch_mode = ECHUIM_DESKTOP;
    511 
    512   const char* modes[] = { "Desktop", "Immersive", "SysLauncher", "??" };
    513 
    514   if (launch_mode_determined)
    515     return launch_mode;
    516 
    517   if (chrome_mode_ != ECHUIM_SYSTEM_LAUNCHER) {
    518     launch_mode = chrome_mode_;
    519     AtlTrace("Launch mode is that of chrome, %s\n", modes[launch_mode]);
    520     launch_mode_determined = true;
    521     return launch_mode;
    522   }
    523 
    524   if (parameters_.HasSwitch(switches::kForceImmersive)) {
    525     launch_mode = ECHUIM_IMMERSIVE;
    526     launch_mode_determined = true;
    527     parameters_ = CommandLine(CommandLine::NO_PROGRAM);
    528   } else if (parameters_.HasSwitch(switches::kForceDesktop)) {
    529     launch_mode = ECHUIM_DESKTOP;
    530     launch_mode_determined = true;
    531     parameters_ = CommandLine(CommandLine::NO_PROGRAM);
    532   }
    533 
    534   base::win::RegKey reg_key;
    535   LONG key_result = reg_key.Create(HKEY_CURRENT_USER,
    536                                    chrome::kMetroRegistryPath,
    537                                    KEY_ALL_ACCESS);
    538   if (key_result != ERROR_SUCCESS) {
    539     AtlTrace("Failed to open HKCU %ls key, error 0x%x\n",
    540              chrome::kMetroRegistryPath,
    541              key_result);
    542     if (!launch_mode_determined) {
    543       launch_mode = ECHUIM_DESKTOP;
    544       launch_mode_determined = true;
    545     }
    546     return launch_mode;
    547   }
    548 
    549   if (launch_mode_determined) {
    550     AtlTrace("Launch mode forced by cmdline to %s\n", modes[launch_mode]);
    551     reg_key.WriteValue(chrome::kLaunchModeValue,
    552                        static_cast<DWORD>(launch_mode));
    553     return launch_mode;
    554   }
    555 
    556   DWORD reg_value;
    557   if (reg_key.ReadValueDW(chrome::kLaunchModeValue,
    558                           &reg_value) != ERROR_SUCCESS) {
    559     launch_mode = ECHUIM_DESKTOP;
    560     AtlTrace("Launch mode forced by heuristics to %s\n", modes[launch_mode]);
    561   } else if (reg_value >= ECHUIM_SYSTEM_LAUNCHER) {
    562     AtlTrace("Invalid registry launch mode value %u\n", reg_value);
    563     launch_mode = ECHUIM_DESKTOP;
    564   } else {
    565     launch_mode = static_cast<EC_HOST_UI_MODE>(reg_value);
    566     AtlTrace("Launch mode forced by registry to %s\n", modes[launch_mode]);
    567   }
    568 
    569   launch_mode_determined = true;
    570   return launch_mode;
    571 }
    572