summaryrefslogtreecommitdiffstats
path: root/chrome/browser/browser_main.cc
blob: 15703a20acdc9197c314a12767499f4068c88401 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <windows.h>
#include <shellapi.h>

#include <algorithm>

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/gfx/vector_canvas.h"
#include "base/histogram.h"
#include "base/path_service.h"
#include "base/registry.h"
#include "base/string_util.h"
#include "base/tracked_objects.h"
#include "base/win_util.h"
#include "chrome/app/result_codes.h"
#include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_init.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_prefs.h"
#include "chrome/browser/browser_process_impl.h"
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/cert_store.h"
#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
#include "chrome/browser/first_run.h"
#include "chrome/browser/jankometer.h"
#include "chrome/browser/metrics_service.h"
#include "chrome/browser/net/dns_global.h"
#include "chrome/browser/plugin_service.h"
#include "chrome/browser/printing/print_job_manager.h"
#include "chrome/browser/rlz/rlz.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/url_fixer_upper.h"
#include "chrome/browser/user_metrics.h"
#include "chrome/browser/views/user_data_dir_dialog.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/env_util.h"
#include "chrome/common/env_vars.h"
#include "chrome/common/jstemplate_builder.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/resource_bundle.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/common/win_util.h"
#include "chrome/installer/util/google_update_settings.h"
#include "chrome/views/accelerator_handler.h"
#include "net/base/net_module.h"
#include "net/base/net_resources.h"
#include "net/base/net_util.h"
#include "net/base/winsock_init.h"
#include "net/http/http_network_layer.h"

#include "chromium_strings.h"
#include "generated_resources.h"

namespace {

// This function provides some ways to test crash and assertion handling
// behavior of the program.
void HandleErrorTestParameters(const CommandLine& command_line) {
  // This parameter causes an assertion.
  if (command_line.HasSwitch(switches::kBrowserAssertTest)) {
    DCHECK(false);
  }

  // This parameter causes a null pointer crash (crash reporter trigger).
  if (command_line.HasSwitch(switches::kBrowserCrashTest)) {
    int* bad_pointer = NULL;
    *bad_pointer = 0;
  }
}

// This is called indirectly by the network layer to access resources.
std::string NetResourceProvider(int key) {
  const std::string& data_blob =
      ResourceBundle::GetSharedInstance().GetDataResource(key);
  if (IDR_DIR_HEADER_HTML == key) {
    DictionaryValue value;
    value.SetString(L"header",
                    l10n_util::GetString(IDS_DIRECTORY_LISTING_HEADER));
    value.SetString(L"parentDirText",
                    l10n_util::GetString(IDS_DIRECTORY_LISTING_PARENT));
    value.SetString(L"headerName",
                    l10n_util::GetString(IDS_DIRECTORY_LISTING_NAME));
    value.SetString(L"headerSize",
                    l10n_util::GetString(IDS_DIRECTORY_LISTING_SIZE));
    value.SetString(L"headerDateModified",
                    l10n_util::GetString(IDS_DIRECTORY_LISTING_DATE_MODIFIED));
    return jstemplate_builder::GetTemplateHtml(data_blob, &value, "t");
  }

  return data_blob;
}

// Displays a warning message if the user is running chrome on windows 2000.
// Returns true if the OS is win2000, false otherwise.
bool CheckForWin2000() {
  if (win_util::GetWinVersion() == win_util::WINVERSION_2000) {
    const std::wstring text = l10n_util::GetString(IDS_UNSUPPORTED_OS_WIN2000);
    const std::wstring caption = l10n_util::GetString(IDS_PRODUCT_NAME);
    win_util::MessageBox(NULL, text, caption,
                         MB_OK | MB_ICONWARNING | MB_TOPMOST);
    return true;
  }
  return false;
}

bool AskForUninstallConfirmation() {
  const std::wstring text = l10n_util::GetString(IDS_UNINSTALL_VERIFY);
  const std::wstring caption = l10n_util::GetString(IDS_PRODUCT_NAME);
  const UINT flags = MB_OKCANCEL | MB_ICONWARNING | MB_TOPMOST;
  return (IDOK == win_util::MessageBox(NULL, text, caption, flags));
}

// Prepares the localized strings that are going to be displayed to
// the user if the browser process dies. These strings are stored in the
// environment block so they are accessible in the early stages of the
// chrome executable's lifetime.
void PrepareRestartOnCrashEnviroment(const CommandLine &parsed_command_line) {
  // Clear this var so child processes don't show the dialog by default.
  ::SetEnvironmentVariableW(env_vars::kShowRestart, NULL);

  // For non-interactive tests we don't restart on crash.
  if (::GetEnvironmentVariableW(env_vars::kHeadless, NULL, 0))
    return;

  // If the known command-line test options are used we don't create the
  // environment block which means we don't get the restart dialog.
  if (parsed_command_line.HasSwitch(switches::kBrowserCrashTest) ||
      parsed_command_line.HasSwitch(switches::kBrowserAssertTest) ||
      parsed_command_line.HasSwitch(switches::kNoErrorDialogs))
    return;

  // The encoding we use for the info is "title|context|direction" where
  // direction is either env_vars::kRtlLocale or env_vars::kLtrLocale depending
  // on the current locale.
  std::wstring dlg_strings;
  dlg_strings.append(l10n_util::GetString(IDS_CRASH_RECOVERY_TITLE));
  dlg_strings.append(L"|");
  dlg_strings.append(l10n_util::GetString(IDS_CRASH_RECOVERY_CONTENT));
  dlg_strings.append(L"|");
  if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
    dlg_strings.append(env_vars::kRtlLocale);
  else
    dlg_strings.append(env_vars::kLtrLocale);

  ::SetEnvironmentVariableW(env_vars::kRestartInfo, dlg_strings.c_str());
}

int DoUninstallTasks() {
  if (!AskForUninstallConfirmation())
    return ResultCodes::UNINSTALL_USER_CANCEL;
  // The following actions are just best effort.
  LOG(INFO) << "Executing uninstall actions";
  ResultCodes::ExitCode ret = ResultCodes::NORMAL_EXIT;
  if (!FirstRun::RemoveSentinel())
    ret = ResultCodes::UNINSTALL_DELETE_FILE_ERROR;
  if (!FirstRun::RemoveChromeDesktopShortcut())
    ret = ResultCodes::UNINSTALL_DELETE_FILE_ERROR;
  if (!FirstRun::RemoveChromeQuickLaunchShortcut())
    ret = ResultCodes::UNINSTALL_DELETE_FILE_ERROR;
  return ret;
}

// This method handles the --hide-icons and --show-icons command line options
// for chrome that get triggered by Windows from registry entries
// HideIconsCommand & ShowIconsCommand. Chrome doesn't support hide icons
// functionality so we just ask the users if they want to uninstall Chrome.
int HandleIconsCommands(const CommandLine &parsed_command_line) {
  if (parsed_command_line.HasSwitch(switches::kHideIcons)) {
    OSVERSIONINFO version = {0};
    version.dwOSVersionInfoSize = sizeof(version);
    if (!GetVersionEx(&version))
      return ResultCodes::UNSUPPORTED_PARAM;

    std::wstring cp_applet;
    if (version.dwMajorVersion >= 6) {
      cp_applet.assign(L"Programs and Features");  // Windows Vista and later.
    } else if (version.dwMajorVersion == 5 && version.dwMinorVersion >= 1) {
      cp_applet.assign(L"Add/Remove Programs");  // Windows XP.
    } else {
      return ResultCodes::UNSUPPORTED_PARAM;  // Not supported on Win2K?
    }

    const std::wstring msg = l10n_util::GetStringF(IDS_HIDE_ICONS_NOT_SUPPORTED,
                                                   cp_applet);
    const std::wstring caption = l10n_util::GetString(IDS_PRODUCT_NAME);
    const UINT flags = MB_OKCANCEL | MB_ICONWARNING | MB_TOPMOST;
    if (IDOK == win_util::MessageBox(NULL, msg, caption, flags))
      ShellExecute(NULL, NULL, L"appwiz.cpl", NULL, NULL, SW_SHOWNORMAL);
    return ResultCodes::NORMAL_EXIT;  // Exit as we are not launching browser.
  }
  // We don't hide icons so we shouldn't do anything special to show them
  return ResultCodes::UNSUPPORTED_PARAM;
}

bool DoUpgradeTasks(const CommandLine& command_line) {
  if (!Upgrade::SwapNewChromeExeIfPresent())
    return false;
  // At this point the chrome.exe has been swapped with the new one.
  if (!Upgrade::RelaunchChromeBrowser(command_line)) {
    // The re-launch fails. Feel free to panic now.
    NOTREACHED();
  }
  return true;
}

bool CreateUniqueChromeEvent() {
  std::wstring exe;
  PathService::Get(base::FILE_EXE, &exe);
  std::replace(exe.begin(), exe.end(), '\\', '!');
  std::transform(exe.begin(), exe.end(), exe.begin(), tolower);
  HANDLE handle = CreateEvent(NULL, TRUE, TRUE, exe.c_str());
  bool already_running = false;
  if (GetLastError() == ERROR_ALREADY_EXISTS) {
    already_running = true;
    CloseHandle(handle);
  }
  return already_running;
}

// We record in UMA the conditions that can prevent breakpad from generating
// and sending crash reports. Namely that the crash reporting registration
// failed and that the process is being debugged.
void RecordBreakpadStatusUMA(MetricsService* metrics) {
  DWORD len = ::GetEnvironmentVariableW(env_vars::kNoOOBreakpad, NULL, 0);
  metrics->RecordBreakpadRegistration((len == 0));
  metrics->RecordBreakpadHasDebugger(TRUE == ::IsDebuggerPresent());
}

}  // namespace

// Main routine for running as the Browser process.
int BrowserMain(CommandLine &parsed_command_line, int show_command,
                sandbox::BrokerServices* broker_services) {
  // WARNING: If we get a WM_ENDSESSION objects created on the stack here
  // are NOT deleted. If you need something to run during WM_ENDSESSION add it
  // to browser_shutdown::Shutdown or BrowserProcess::EndSession.

  // TODO(beng, brettw): someday, break this out into sub functions with well
  //                     defined roles (e.g. pre/post-profile startup, etc).

  MessageLoop main_message_loop(MessageLoop::TYPE_UI);

  std::wstring app_name = chrome::kBrowserAppName;
  std::string thread_name_string = WideToASCII(app_name + L"_BrowserMain");

  const char* thread_name = thread_name_string.c_str();
  PlatformThread::SetName(thread_name);
  main_message_loop.set_thread_name(thread_name);
  bool already_running = CreateUniqueChromeEvent();

  // Make the selection of network stacks early on before any consumers try to
  // issue HTTP requests.
  if (parsed_command_line.HasSwitch(switches::kUseNewHttp))
    net::HttpNetworkLayer::UseWinHttp(false);

  std::wstring user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  BrowserInit::MessageWindow message_window(user_data_dir);

  scoped_ptr<BrowserProcess> browser_process;
  if (parsed_command_line.HasSwitch(switches::kImport)) {
    // We use different BrowserProcess when importing so no GoogleURLTracker is
    // instantiated (as it makes a URLRequest and we don't have an IO thread,
    // see bug #1292702).
    browser_process.reset(new FirstRunBrowserProcess(parsed_command_line));
  } else {
    browser_process.reset(new BrowserProcessImpl(parsed_command_line));
  }

  // BrowserProcessImpl's constructor should set g_browser_process.
  DCHECK(g_browser_process);

  // Load local state.  This includes the application locale so we know which
  // locale dll to load.
  PrefService* local_state = browser_process->local_state();
  DCHECK(local_state);

  bool is_first_run = FirstRun::IsChromeFirstRun() ||
      parsed_command_line.HasSwitch(switches::kFirstRun);

  // Initialize ResourceBundle which handles files loaded from external
  // sources. This has to be done before uninstall code path and before prefs
  // are registered.
  local_state->RegisterStringPref(prefs::kApplicationLocale, L"");
  local_state->RegisterBooleanPref(prefs::kMetricsReportingEnabled, false);

  // During first run we read the google update registry key to find what
  // language the user selected when downloading the installer. This
  // becomes our default language in the prefs.
  if (is_first_run) {
    std::wstring install_lang;
    if (GoogleUpdateSettings::GetLanguage(&install_lang))
      local_state->SetString(prefs::kApplicationLocale, install_lang);
    if (GoogleUpdateSettings::GetCollectStatsConsent())
      local_state->SetBoolean(prefs::kMetricsReportingEnabled, true);
  }

  ResourceBundle::InitSharedInstance(
      local_state->GetString(prefs::kApplicationLocale));
  // We only load the theme dll in the browser process.
  ResourceBundle::GetSharedInstance().LoadThemeResources();

  if (!parsed_command_line.HasSwitch(switches::kNoErrorDialogs)) {
    // Display a warning if the user is running windows 2000.
    CheckForWin2000();
  }

  // Initialize histogram statistics gathering system.
  StatisticsRecorder statistics;

  // Strart tracking the creation and deletion of Task instances
  bool tracking_objects = false;
#ifdef TRACK_ALL_TASK_OBJECTS
  tracking_objects = tracked_objects::ThreadData::StartTracking(true);
#endif

  // Try to create/load the profile.
  ProfileManager* profile_manager = browser_process->profile_manager();
  Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
  if (!profile) {
    user_data_dir = UserDataDirDialog::RunUserDataDirDialog(user_data_dir);
    // Flush the message loop which lets the UserDataDirDialog close.
    MessageLoop::current()->Run();

    ResourceBundle::CleanupSharedInstance();

    if (!user_data_dir.empty()) {
      // Because of the way CommandLine parses, it's sufficient to append a new
      // --user-data-dir switch.  The last flag of the same name wins.
      // TODO(tc): It would be nice to remove the flag we don't want, but that
      // sounds risky if we parse differently than CommandLineToArgvW.
      std::wstring new_command_line =
          parsed_command_line.command_line_string();
      CommandLine::AppendSwitchWithValue(&new_command_line,
          switches::kUserDataDir, user_data_dir);
      process_util::LaunchApp(new_command_line, false, false, NULL);
    }

    return ResultCodes::NORMAL_EXIT;
  }

  PrefService* user_prefs = profile->GetPrefs();
  DCHECK(user_prefs);

  // Now that local state and user prefs have been loaded, make the two pref
  // services aware of all our preferences.
  browser::RegisterAllPrefs(user_prefs, local_state);

  // Record last shutdown time into a histogram.
  browser_shutdown::ReadLastShutdownInfo();

  // If the command line specifies 'uninstall' then we need to work here
  // unless we detect another chrome browser running.
  if (parsed_command_line.HasSwitch(switches::kUninstall)) {
    if (already_running) {
      const std::wstring text = l10n_util::GetString(IDS_UNINSTALL_CLOSE_APP);
      const std::wstring caption = l10n_util::GetString(IDS_PRODUCT_NAME);
      win_util::MessageBox(NULL, text, caption,
                           MB_OK | MB_ICONWARNING | MB_TOPMOST);
      return ResultCodes::UNINSTALL_CHROME_ALIVE;
    } else {
      return DoUninstallTasks();
    }
  }

  if (parsed_command_line.HasSwitch(switches::kHideIcons) ||
      parsed_command_line.HasSwitch(switches::kShowIcons)) {
    return HandleIconsCommands(parsed_command_line);
  } else if (parsed_command_line.HasSwitch(switches::kMakeDefaultBrowser)) {
    if (ShellIntegration::SetAsDefaultBrowser()) {
      return ResultCodes::NORMAL_EXIT;
    } else {
      return ResultCodes::SHELL_INTEGRATION_FAILED;
    }
  }

  // Importing other browser settings is done in a browser-like process
  // that exits when this task has finished.
  if (parsed_command_line.HasSwitch(switches::kImport))
    return FirstRun::ImportWithUI(profile, parsed_command_line);

  // When another process is running, use it instead of starting us.
  if (message_window.NotifyOtherProcess(show_command))
    return ResultCodes::NORMAL_EXIT;

  // Sometimes we end up killing browser process (http://b/1308130) so make
  // sure we recreate unique event to indicate running browser process.
  message_window.HuntForZombieChromeProcesses();
  CreateUniqueChromeEvent();

  // Do the tasks if chrome has been upgraded while it was last running.
  if (DoUpgradeTasks(parsed_command_line)) {
    return ResultCodes::NORMAL_EXIT;
  }

  message_window.Create();

  // Show the First Run UI if this is the first time Chrome has been run on
  // this computer, or we're being compelled to do so by a command line flag.
  // Note that this be done _after_ the PrefService is initialized and all
  // preferences are registered, since some of the code that the importer
  // touches reads preferences.
  if (is_first_run) {
    // We need to avoid dispatching new tabs when we are doing the import
    // because that will lead to data corruption or a crash. Lock() does that.
    message_window.Lock();
    OpenFirstRunDialog(profile);
    message_window.Unlock();
  }

  // Sets things up so that if we crash from this point on, a dialog will
  // popup asking the user to restart chrome. It is done this late to avoid
  // testing against a bunch of special cases that are taken care early on.
  PrepareRestartOnCrashEnviroment(parsed_command_line);

  // Initialize Winsock.
  net::WinsockInit init;

  // Initialize the DNS prefetch system
  chrome_browser_net::DnsPrefetcherInit dns_prefetch_init(user_prefs);
  chrome_browser_net::DnsPretchHostNamesAtStartup(user_prefs, local_state);

  // Init common control sex.
  INITCOMMONCONTROLSEX config;
  config.dwSize = sizeof(config);
  config.dwICC = ICC_WIN95_CLASSES;
  InitCommonControlsEx(&config);

  win_util::ScopedCOMInitializer com_initializer;

  // Init the RLZ library. This just binds the dll and schedules a task on the
  // file thread to be run sometime later. If this is the first run we record
  // the installation event.
  RLZTracker::InitRlzDelayed(base::DIR_MODULE, is_first_run);

  // Config the network module so it has access to resources.
  net::NetModule::SetResourceProvider(NetResourceProvider);

  // Register our global network handler for chrome-resource:// URLs.
  RegisterURLRequestChromeJob();

  // TODO(brettw): we may want to move this to the browser window somewhere so
  // that if it pops up a dialog box, the user gets it as the child of the
  // browser window instead of a disembodied floating box blocking startup.
  ShellIntegration::VerifyInstallation();

  browser_process->InitBrokerServices(broker_services);
  
  // In unittest mode, this will do nothing.  In normal mode, this will create
  // the global GoogleURLTracker instance, which will promptly go to sleep for
  // five seconds (to avoid slowing startup), and wake up afterwards to see if
  // it should do anything else.  If we don't cause this creation now, it won't
  // happen until someone else asks for the tracker, at which point we may no
  // longer want to sleep for five seconds.
  //
  // A simpler way of doing all this would be to have some function which could
  // give the time elapsed since startup, and simply have the tracker check that
  // when asked to initialize itself, but this doesn't seem to exist.
  //
  // This can't be created in the BrowserProcessImpl constructor because it
  // needs to read prefs that get set after that runs.
  browser_process->google_url_tracker();

  // Have Chrome plugins write their data to the profile directory.
  PluginService::GetInstance()->SetChromePluginDataDir(profile->GetPath());

  // Initialize the CertStore.
  CertStore::Initialize();

  MetricsService* metrics = NULL;
  if (!parsed_command_line.HasSwitch(switches::kDisableMetrics)) {
    if (parsed_command_line.HasSwitch(switches::kDisableMetricsReporting)) {
      local_state->transient()->SetBoolean(prefs::kMetricsReportingEnabled,
                                           false);
    }
    metrics = browser_process->metrics_service();
    DCHECK(metrics);
    // Start user experience metrics recording, if enabled.
    metrics->SetRecording(local_state->GetBoolean(prefs::kMetricsIsRecording));
  }
  InstallJankometer(parsed_command_line);

  if (parsed_command_line.HasSwitch(switches::kDebugPrint)) {
    browser_process->print_job_manager()->set_debug_dump_path(
        parsed_command_line.GetSwitchValue(switches::kDebugPrint));
  }

  HandleErrorTestParameters(parsed_command_line);

  RecordBreakpadStatusUMA(metrics);

  int result_code = ResultCodes::NORMAL_EXIT;
  if (BrowserInit::ProcessCommandLine(parsed_command_line, L"", local_state,
                                      show_command, true, profile,
                                      &result_code)) {
    MessageLoopForUI::current()->Run(browser_process->accelerator_handler());
  }

  if (metrics)
    metrics->SetRecording(false);  // Force persistent save.

  // browser_shutdown takes care of deleting browser_process, so we need to
  // release it.
  browser_process.release();

  browser_shutdown::Shutdown();

  // The following teardown code will pacify Purify, but is not necessary for
  // shutdown.  Only list methods here that have no significant side effects
  // and can be run in single threaded mode before terminating.
#ifndef NDEBUG  // Don't call these in a Release build: they just waste time.
  // The following should ONLY be called when in single threaded mode. It is
  // unsafe to do this cleanup if other threads are still active.
  // It is also very unnecessary, so I'm only doing this in debug to satisfy
  // purify.
  if (tracking_objects)
    tracked_objects::ThreadData::ShutdownSingleThreadedCleanup();
#endif  // NDEBUG

  return result_code;
}