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