[OOBE] Introduce CoreOobe

Move the browser/business logic away from CoreOobeHandler into a new
class that will host it - CoreOobe. This is line with the rest of the
architecture of OOBE that relies on  ScreenHandlers & ScreenViews.

Bug: b:266939445

Change-Id: I10d301e3b57ba97294578896d467375e0b20f9c5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4525996
Commit-Queue: Renato Silva <rrsilva@google.com>
Reviewed-by: Danila Kuzmin <dkuzmin@google.com>
Cr-Commit-Position: refs/heads/main@{#1145570}
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 73a13925..33a67fe8 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -1729,6 +1729,8 @@
     "login/screens/chromevox_hint/chromevox_hint_detector.h",
     "login/screens/consolidated_consent_screen.cc",
     "login/screens/consolidated_consent_screen.h",
+    "login/screens/core_oobe.cc",
+    "login/screens/core_oobe.h",
     "login/screens/cryptohome_recovery_screen.cc",
     "login/screens/cryptohome_recovery_screen.h",
     "login/screens/cryptohome_recovery_setup_screen.cc",
diff --git a/chrome/browser/ash/login/screens/core_oobe.cc b/chrome/browser/ash/login/screens/core_oobe.cc
new file mode 100644
index 0000000..28353bc
--- /dev/null
+++ b/chrome/browser/ash/login/screens/core_oobe.cc
@@ -0,0 +1,249 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/login/screens/core_oobe.h"
+#include "ash/public/cpp/shelf_config.h"
+#include "ash/public/cpp/tablet_mode.h"
+#include "ash/shell.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback_forward.h"
+#include "build/branding_buildflags.h"
+#include "chrome/browser/ash/login/configuration_keys.h"
+#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
+#include "chrome/browser/ash/system/input_device_settings.h"
+#include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h"
+#include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
+#include "chrome/common/channel_info.h"
+#include "components/version_info/channel.h"
+#include "ui/display/screen.h"
+
+namespace ash {
+
+CoreOobe::CoreOobe(const std::string& display_type,
+                   base::WeakPtr<CoreOobeView> view)
+    : view_(view) {
+  is_oobe_display_ = display_type == OobeUI::kOobeDisplay;
+
+  TabletMode::Get()->AddObserver(this);
+  OobeConfiguration::Get()->AddAndFireObserver(this);
+  ChromeKeyboardControllerClient::Get()->AddObserver(this);
+
+  OnKeyboardVisibilityChanged(
+      ChromeKeyboardControllerClient::Get()->is_keyboard_visible());
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  version_info_updater_.StartUpdate(true);
+#else
+  version_info_updater_.StartUpdate(false);
+#endif
+
+  UpdateClientAreaSize(
+      display::Screen::GetScreen()->GetPrimaryDisplay().size());
+
+  // Don't show version label on the stable and beta channels by default.
+  version_info::Channel channel = chrome::GetChannel();
+  if (channel != version_info::Channel::STABLE &&
+      channel != version_info::Channel::BETA) {
+    if (view_) {
+      view_->ToggleSystemInfo();
+    }
+  }
+
+  if (ash::system::InputDeviceSettings::Get()
+          ->ForceKeyboardDrivenUINavigation()) {
+    if (view_) {
+      view_->EnableKeyboardFlow();
+    }
+  }
+}
+
+CoreOobe::~CoreOobe() {
+  OobeConfiguration::Get()->RemoveObserver(this);
+
+  // Ash may be released before us.
+  if (TabletMode::Get()) {
+    TabletMode::Get()->RemoveObserver(this);
+  }
+
+  if (ChromeKeyboardControllerClient::Get()) {
+    ChromeKeyboardControllerClient::Get()->RemoveObserver(this);
+  }
+}
+
+void CoreOobe::ShowScreenWithData(const OobeScreenId& screen,
+                                  absl::optional<base::Value::Dict> data) {
+  // Defer until fully initialized.
+  if (ui_init_state_ != CoreOobeView::UiState::kFullyInitialized) {
+    pending_calls_.show_screen_with_data =
+        base::BindOnce(&CoreOobe::ShowScreenWithData, base::Unretained(this),
+                       screen, std::move(data));
+    return;
+  }
+
+  // Decide whether or not to show the screen based on availability.
+  if (view_) {
+    view_->ShowScreenWithData(screen, std::move(data));
+  }
+}
+
+void CoreOobe::ReloadContent() {
+  // Defer until fully initialized.
+  if (ui_init_state_ != CoreOobeView::UiState::kFullyInitialized) {
+    pending_calls_.reload_content =
+        base::BindOnce(&CoreOobe::ReloadContent, base::Unretained(this));
+    return;
+  }
+
+  if (view_) {
+    view_->ReloadContent();
+  }
+}
+
+void CoreOobe::ForwardCancel() {
+  // Defer until fully initialized.
+  if (ui_init_state_ != CoreOobeView::UiState::kFullyInitialized) {
+    pending_calls_.forward_cancel =
+        base::BindOnce(&CoreOobe::ForwardCancel, base::Unretained(this));
+    return;
+  }
+
+  if (view_) {
+    view_->ForwardCancel();
+  }
+}
+
+void CoreOobe::UpdateClientAreaSize(const gfx::Size& size) {
+  if (!view_) {
+    return;
+  }
+  view_->SetShelfHeight(ShelfConfig::Get()->shelf_size());
+
+  const gfx::Size display_size =
+      display::Screen::GetScreen()->GetPrimaryDisplay().size();
+  const bool is_horizontal = display_size.width() > display_size.height();
+  view_->SetOrientation(is_horizontal);
+
+  const gfx::Size dialog_size = CalculateOobeDialogSize(
+      size, ShelfConfig::Get()->shelf_size(), is_horizontal);
+  view_->SetDialogSize(dialog_size.width(), dialog_size.height());
+}
+
+void CoreOobe::TriggerDown() {
+  if (view_) {
+    view_->TriggerDown();
+  }
+}
+
+void CoreOobe::ToggleSystemInfo() {
+  if (view_) {
+    view_->ToggleSystemInfo();
+  }
+}
+
+void CoreOobe::LaunchHelpApp(int help_topic_id) {
+  if (!help_app_.get()) {
+    help_app_ = new HelpAppLauncher(
+        LoginDisplayHost::default_host()->GetNativeWindow());
+  }
+  help_app_->ShowHelpTopic(
+      static_cast<HelpAppLauncher::HelpTopic>(help_topic_id));
+}
+
+void CoreOobe::OnOSVersionLabelTextUpdated(const std::string& os_version_text) {
+  if (view_) {
+    view_->SetOsVersionLabelText(os_version_text);
+  }
+}
+
+void CoreOobe::OnDeviceInfoUpdated(const std::string& bluetooth_name) {
+  if (view_) {
+    view_->SetBluetoothDeviceInfo(bluetooth_name);
+  }
+}
+
+void CoreOobe::OnKeyboardVisibilityChanged(bool shown) {
+  if (view_) {
+    view_->SetVirtualKeyboardShown(shown);
+  }
+}
+
+void CoreOobe::OnTabletModeStarted() {
+  OnTabletModeChanged(/*tablet_mode_enabled=*/true);
+}
+
+void CoreOobe::OnTabletModeEnded() {
+  OnTabletModeChanged(/*tablet_mode_enabled=*/false);
+}
+
+void CoreOobe::OnTabletModeChanged(bool tablet_mode_enabled) {
+  // Defer until fully initialized.
+  if (ui_init_state_ != CoreOobeView::UiState::kFullyInitialized) {
+    pending_calls_.on_tablet_mode_changed =
+        base::BindOnce(&CoreOobe::OnTabletModeChanged, base::Unretained(this),
+                       tablet_mode_enabled);
+    return;
+  }
+
+  if (view_) {
+    view_->SetTabletModeState(tablet_mode_enabled);
+  }
+}
+
+void CoreOobe::OnOobeConfigurationChanged() {
+  // Defer until fully initialized.
+  if (ui_init_state_ != CoreOobeView::UiState::kFullyInitialized) {
+    pending_calls_.on_oobe_configuration_changed = base::BindOnce(
+        &CoreOobe::OnOobeConfigurationChanged, base::Unretained(this));
+    return;
+  }
+
+  if (view_) {
+    view_->UpdateOobeConfiguration();
+  }
+}
+
+void CoreOobe::UpdateUiInitState(CoreOobeView::UiState state) {
+  switch (state) {
+    case CoreOobeView::UiState::kUninitialized:
+      NOTREACHED();
+      break;
+    case CoreOobeView::UiState::kCoreHandlerInitialized:
+      // JavaScript is now allowed in the handler.
+      CHECK(ui_init_state_ == CoreOobeView::UiState::kUninitialized);
+      ui_init_state_ = CoreOobeView::UiState::kCoreHandlerInitialized;
+      break;
+    case CoreOobeView::UiState::kFullyInitialized:
+      // OOBE is fully loaded.
+      CHECK(ui_init_state_ == CoreOobeView::UiState::kCoreHandlerInitialized);
+      ui_init_state_ = CoreOobeView::UiState::kFullyInitialized;
+      LoginDisplayHost::default_host()->GetOobeUI()->InitializeHandlers();
+      ExecutePendingCalls();
+      break;
+  }
+}
+
+void CoreOobe::ExecutePendingCalls() {
+  CHECK(ui_init_state_ == CoreOobeView::UiState::kFullyInitialized);
+  if (pending_calls_.on_tablet_mode_changed) {
+    std::move(pending_calls_.on_tablet_mode_changed).Run();
+  }
+  if (pending_calls_.on_oobe_configuration_changed) {
+    std::move(pending_calls_.on_oobe_configuration_changed).Run();
+  }
+  if (pending_calls_.show_screen_with_data) {
+    std::move(pending_calls_.show_screen_with_data).Run();
+  }
+  if (pending_calls_.reload_content) {
+    std::move(pending_calls_.reload_content).Run();
+  }
+  if (pending_calls_.forward_cancel) {
+    std::move(pending_calls_.forward_cancel).Run();
+  }
+}
+
+CoreOobe::PendingFrontendCalls::PendingFrontendCalls() = default;
+CoreOobe::PendingFrontendCalls::~PendingFrontendCalls() = default;
+
+}  // namespace ash
diff --git a/chrome/browser/ash/login/screens/core_oobe.h b/chrome/browser/ash/login/screens/core_oobe.h
new file mode 100644
index 0000000..e8058e0
--- /dev/null
+++ b/chrome/browser/ash/login/screens/core_oobe.h
@@ -0,0 +1,142 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_LOGIN_SCREENS_CORE_OOBE_H_
+#define CHROME_BROWSER_ASH_LOGIN_SCREENS_CORE_OOBE_H_
+
+#include "ash/public/cpp/tablet_mode_observer.h"
+#include "base/values.h"
+#include "chrome/browser/ash/login/help_app_launcher.h"
+#include "chrome/browser/ash/login/oobe_configuration.h"
+#include "chrome/browser/ash/login/oobe_screen.h"
+#include "chrome/browser/ash/login/version_info_updater.h"
+#include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
+#include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h"
+#include "ui/events/event_source.h"
+
+namespace ash {
+
+/**
+ * --- CoreOobe ---
+ * Holds all the logic of the underlying OOBE structure that hosts the screens.
+ * The actual UI part is controlled by (CoreOobeHandler & CoreOobeView) creating
+ * a separation of the browser logic and the actual renderer implementation of
+ * it. It can be thought of as a 'BaseScreen' in OOBE's architecture.
+ *
+ *  ------------------------------------------------
+ *  | Browser / Business Logic |   Renderer / UI   |
+ *  ------------------------------------------------
+ *  |      BaseScreen          | BaseScreenHandler | All OOBE screens follow
+ *  |                          |        View       | this pattern % exceptions
+ *  ------------------------------------------------
+ *  |       CoreOobe           |  CoreOobeHandler  | Main OOBE structure that
+ *  |                          |   CoreOobeView    | hosts the screens in the UI
+ *  ------------------------------------------------
+ *
+ *  Since the initialization of the UI isn't instantaneous, CoreOobeView has the
+ *  notion of a |CoreOobeView::UiState| and informs this class whenever it
+ *  changes. The possible states are:
+ *
+ *   - kUninitialized -
+ *   When this class is created and the UI is still executing the import
+ *   dependencies in |oobe.js|. CoreOobeHandler is not allowed to send JS yet.
+ *
+ *   - kCoreHandlerInitialized -
+ *   The HTML document has been parsed and all the JavaScript imports from
+ *   |oobe.js| have been executed. This state is triggered in the first line of
+ *   |oobe.js| and at this point the |CoreOobeHandler| is allowed to send JS,
+ *   but limited to very basic calls.
+ *
+ *   - kFullyInitialized -
+ *   The frontend is fully initialized and all screens have been added to the
+ *   HTML document. Some calls to this class that require the full UI to be
+ *   ready will be deferred until we reach this state.
+ */
+
+class CoreOobeView;
+class PendingFrontendCalls;
+
+class CoreOobe : public VersionInfoUpdater::Delegate,
+                 public TabletModeObserver,
+                 public OobeConfiguration::Observer,
+                 public ChromeKeyboardControllerClient::Observer {
+ public:
+  explicit CoreOobe(const std::string& display_type,
+                    base::WeakPtr<CoreOobeView> view);
+  ~CoreOobe() override;
+  CoreOobe(const CoreOobe&) = delete;
+  CoreOobe& operator=(const CoreOobe&) = delete;
+
+  // Calls to these methods will be deferred until fully initialized.
+  // See |CoreOobeView::UiState| for details.
+  void ShowScreenWithData(const OobeScreenId& screen,
+                          absl::optional<base::Value::Dict> data);
+  void ReloadContent();
+  void ForwardCancel();
+
+  // These methods can be called at any state of the initialization.
+  // See |CoreOobeView::UiState| for details.
+  void UpdateClientAreaSize(const gfx::Size& size);
+  void TriggerDown();
+  void ToggleSystemInfo();
+  void LaunchHelpApp(int help_topic_id);
+
+ protected:
+  // VersionInfoUpdater::Delegate implementation:
+  void OnOSVersionLabelTextUpdated(
+      const std::string& os_version_label_text) override;
+  void OnDeviceInfoUpdated(const std::string& bluetooth_name) override;
+  void OnEnterpriseInfoUpdated(const std::string& message_text,
+                               const std::string& asset_id) override {}
+  void OnAdbSideloadStatusUpdated(bool enabled) override {}
+
+  // ChromeKeyboardControllerClient::Observer:
+  void OnKeyboardVisibilityChanged(bool visible) override;
+
+  // TabletModeObserver:
+  void OnTabletModeStarted() override;
+  void OnTabletModeEnded() override;
+  void OnTabletModeChanged(bool tablet_mode_enabled);
+
+  // OobeConfiguration::Observer:
+  void OnOobeConfigurationChanged() override;
+
+ private:
+  // Called by the |CoreOobeHandler| to update the initialization state.
+  friend class CoreOobeHandler;
+  void UpdateUiInitState(CoreOobeView::UiState state);
+
+  // Methods that need the full OOBE to be initialized will deferred until
+  // the |CoreOobeHandler| notifies us with |UiState::kFullyInitialized|
+  void ExecutePendingCalls();
+
+  class PendingFrontendCalls {
+   public:
+    PendingFrontendCalls();
+    ~PendingFrontendCalls();
+
+    base::OnceClosure on_tablet_mode_changed;
+    base::OnceClosure on_oobe_configuration_changed;
+    base::OnceClosure show_screen_with_data;
+    base::OnceClosure reload_content;
+    base::OnceClosure forward_cancel;
+  };
+  PendingFrontendCalls pending_calls_;
+
+  // Updates when version info is changed.
+  VersionInfoUpdater version_info_updater_{this};
+
+  // Help application used for help dialogs.
+  scoped_refptr<HelpAppLauncher> help_app_;
+
+  bool is_oobe_display_ = false;
+
+  CoreOobeView::UiState ui_init_state_ = CoreOobeView::UiState::kUninitialized;
+
+  base::WeakPtr<CoreOobeView> view_;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_LOGIN_SCREENS_CORE_OOBE_H_
diff --git a/chrome/browser/ash/login/ui/login_display_host_common.cc b/chrome/browser/ash/login/ui/login_display_host_common.cc
index 08e0309..daa74d0 100644
--- a/chrome/browser/ash/login/ui/login_display_host_common.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_common.cc
@@ -476,7 +476,7 @@
     if (!GetOobeUI()) {
       return false;
     }
-    GetOobeUI()->GetCoreOobeView()->ForwardCancel();
+    GetOobeUI()->GetCoreOobe()->ForwardCancel();
     return true;
   }
 
diff --git a/chrome/browser/ash/login/ui/login_display_host_webui.cc b/chrome/browser/ash/login/ui/login_display_host_webui.cc
index a4bd2012..8d5b6e1 100644
--- a/chrome/browser/ash/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_webui.cc
@@ -693,8 +693,7 @@
   }
 
   if (GetOobeUI()) {
-    GetOobeUI()->GetCoreOobeView()->UpdateClientAreaSize(
-        primary_display.size());
+    GetOobeUI()->GetCoreOobe()->UpdateClientAreaSize(primary_display.size());
     if (changed_metrics & DISPLAY_METRIC_PRIMARY)
       GetOobeUI()->OnDisplayConfigurationChanged();
   }
@@ -720,7 +719,7 @@
 void LoginDisplayHostWebUI::FinishBootingAnimation() {
   CHECK(features::IsOobeSimonEnabled());
   ash::Shell::Get()->booting_animation_controller()->Finish();
-  GetOobeUI()->GetCoreOobeView()->TriggerDown();
+  GetOobeUI()->GetCoreOobe()->TriggerDown();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -799,7 +798,7 @@
     if (!GetOobeUI()) {
       return false;
     }
-    GetOobeUI()->GetCoreOobeView()->ToggleSystemInfo();
+    GetOobeUI()->GetCoreOobe()->ToggleSystemInfo();
     return true;
   }
 
diff --git a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc
index 83273d5c..07abaec 100644
--- a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc
@@ -413,7 +413,7 @@
 void OobeUIDialogDelegate::OnViewBoundsChanged(views::View* observed_view) {
   if (!widget_)
     return;
-  GetOobeUI()->GetCoreOobeView()->UpdateClientAreaSize(
+  GetOobeUI()->GetCoreOobe()->UpdateClientAreaSize(
       layout_view_->GetContentsBounds().size());
 }
 
diff --git a/chrome/browser/resources/chromeos/login/oobe.js b/chrome/browser/resources/chromeos/login/oobe.js
index ac4152c..68b468b 100644
--- a/chrome/browser/resources/chromeos/login/oobe.js
+++ b/chrome/browser/resources/chromeos/login/oobe.js
@@ -25,6 +25,7 @@
 
 // Everything has been imported at this point.
 traceExecution(TraceEvent.FIRST_LINE_AFTER_IMPORTS);
+chrome.send('initializeCoreHandler');
 
 // Create the global values attached to `window` that are used
 // for accessing OOBE controls from the browser side.
diff --git a/chrome/browser/ui/webui/ash/login/base_screen_handler.cc b/chrome/browser/ui/webui/ash/login/base_screen_handler.cc
index 1d456d5..3cdfe60 100644
--- a/chrome/browser/ui/webui/ash/login/base_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/base_screen_handler.cc
@@ -35,8 +35,7 @@
   if (!GetOobeUI()) {
     return;
   }
-  GetOobeUI()->GetCoreOobeView()->ShowScreenWithData(oobe_screen_,
-                                                     std::move(data));
+  GetOobeUI()->GetCoreOobe()->ShowScreenWithData(oobe_screen_, std::move(data));
 }
 
 void BaseScreenHandler::RegisterMessages() {
diff --git a/chrome/browser/ui/webui/ash/login/base_webui_handler.cc b/chrome/browser/ui/webui/ash/login/base_webui_handler.cc
index 1803ed1..5e9a2e8 100644
--- a/chrome/browser/ui/webui/ash/login/base_webui_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/base_webui_handler.cc
@@ -44,7 +44,7 @@
 void BaseWebUIHandler::ShowScreenDeprecated(OobeScreenId screen) {
   if (!GetOobeUI())
     return;
-  GetOobeUI()->GetCoreOobeView()->ShowScreenWithData(screen, absl::nullopt);
+  GetOobeUI()->GetCoreOobe()->ShowScreenWithData(screen, absl::nullopt);
 }
 
 OobeUI* BaseWebUIHandler::GetOobeUI() {
diff --git a/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc b/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc
index f997433..ae4adff 100644
--- a/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc
@@ -7,47 +7,24 @@
 #include <type_traits>
 #include <utility>
 
-#include "ash/constants/ash_features.h"
-#include "ash/public/cpp/event_rewriter_controller.h"
-#include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/tablet_mode.h"
 #include "ash/shell.h"
-#include "base/command_line.h"
-#include "base/functional/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "build/branding_buildflags.h"
 #include "chrome/browser/ash/login/configuration_keys.h"
-#include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
-#include "chrome/browser/ash/login/helper.h"
-#include "chrome/browser/ash/login/lock/screen_locker.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
-#include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
-#include "chrome/browser/ash/system/input_device_settings.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/ui/ash/ash_util.h"
-#include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
-#include "chrome/browser/ui/webui/ash/login/demo_setup_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/common/channel_info.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/login/base_screen_handler_utils.h"
 #include "components/login/localized_values_builder.h"
-#include "components/prefs/pref_service.h"
 #include "components/strings/grit/components_strings.h"
-#include "components/version_info/version_info.h"
 #include "google_apis/google_api_keys.h"
 #include "ui/aura/window_tree_host.h"
-#include "ui/display/screen.h"
 #include "ui/events/event_sink.h"
-#include "ui/gfx/geometry/size.h"
 
 // Enable VLOG level 1.
 #undef ENABLED_VLOG_LEVEL
@@ -55,48 +32,9 @@
 
 namespace ash {
 
-CoreOobeHandler::CoreOobeHandler(const std::string& display_type) {
-  is_oobe_display_ = display_type == OobeUI::kOobeDisplay;
+CoreOobeHandler::CoreOobeHandler() = default;
 
-  TabletMode::Get()->AddObserver(this);
-
-  OobeConfiguration::Get()->AddAndFireObserver(this);
-
-  ChromeKeyboardControllerClient::Get()->AddObserver(this);
-
-  OnKeyboardVisibilityChanged(
-      ChromeKeyboardControllerClient::Get()->is_keyboard_visible());
-
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  version_info_updater_.StartUpdate(true);
-#else
-  version_info_updater_.StartUpdate(false);
-#endif
-  UpdateClientAreaSize(
-      display::Screen::GetScreen()->GetPrimaryDisplay().size());
-
-  // Don't show version label on the stable and beta channels by default.
-  version_info::Channel channel = chrome::GetChannel();
-  if (channel != version_info::Channel::STABLE &&
-      channel != version_info::Channel::BETA) {
-    ToggleSystemInfo();
-  }
-
-  if (ash::system::InputDeviceSettings::Get()
-          ->ForceKeyboardDrivenUINavigation())
-    CallJS("cr.ui.Oobe.enableKeyboardFlow", true);
-}
-
-CoreOobeHandler::~CoreOobeHandler() {
-  OobeConfiguration::Get()->RemoveObserver(this);
-
-  // Ash may be released before us.
-  if (TabletMode::Get())
-    TabletMode::Get()->RemoveObserver(this);
-
-  if (ChromeKeyboardControllerClient::Get())
-    ChromeKeyboardControllerClient::Get()->RemoveObserver(this);
-}
+CoreOobeHandler::~CoreOobeHandler() = default;
 
 void CoreOobeHandler::DeclareLocalizedValues(
     ::login::LocalizedValuesBuilder* builder) {
@@ -110,7 +48,7 @@
 
   const bool has_api_keys_configured = google_apis::HasAPIKeyConfigured() &&
                                        google_apis::HasOAuthClientConfigured();
-  if (!has_api_keys_configured && is_oobe_display_) {
+  if (!has_api_keys_configured) {
     builder->AddF("missingAPIKeysNotice", IDS_LOGIN_API_KEYS_NOTICE,
                   base::ASCIIToUTF16(google_apis::kAPIKeysDevelopersHowToURL));
   }
@@ -122,17 +60,12 @@
   builder->Add("next", IDS_EULA_NEXT_BUTTON);
 }
 
-void CoreOobeHandler::GetAdditionalParameters(base::Value::Dict* dict) {
-  dict->Set("isInTabletMode", TabletMode::Get()->InTabletMode());
-  dict->Set("isDemoModeEnabled", DemoSetupController::IsDemoModeAllowed());
-  if (policy::EnrollmentRequisitionManager::IsMeetDevice()) {
-    // The value is used to show a different UI for this type of the devices.
-    dict->Set("deviceFlowType", "meet");
-  }
-}
-
 void CoreOobeHandler::DeclareJSCallbacks() {
-  AddCallback("screenStateInitialize", &CoreOobeHandler::HandleInitialized);
+  AddCallback("initializeCoreHandler",
+              &CoreOobeHandler::HandleInitializeCoreHandler);
+  AddCallback("screenStateInitialize",
+              &CoreOobeHandler::HandleScreenStateInitialize);
+
   AddCallback("updateCurrentScreen",
               &CoreOobeHandler::HandleUpdateCurrentScreen);
   AddCallback("backdropLoaded", &CoreOobeHandler::HandleBackdropLoaded);
@@ -143,24 +76,114 @@
   AddCallback("enableShelfButtons", &CoreOobeHandler::HandleEnableShelfButtons);
 }
 
+void CoreOobeHandler::GetAdditionalParameters(base::Value::Dict* dict) {
+  dict->Set("isInTabletMode", TabletMode::Get()->InTabletMode());
+  dict->Set("isDemoModeEnabled", DemoSetupController::IsDemoModeAllowed());
+  if (policy::EnrollmentRequisitionManager::IsMeetDevice()) {
+    // The value is used to show a different UI for this type of the devices.
+    dict->Set("deviceFlowType", "meet");
+  }
+}
+
+ui::EventSink* CoreOobeHandler::GetEventSink() {
+  return Shell::GetPrimaryRootWindow()->GetHost()->GetEventSink();
+}
+
 void CoreOobeHandler::ShowScreenWithData(
     const OobeScreenId& screen,
     absl::optional<base::Value::Dict> data) {
+  CHECK(ui_init_state_ == UiState::kFullyInitialized);
+
   base::Value::Dict screen_params;
   screen_params.Set("id", screen.name);
   if (data.has_value()) {
     screen_params.Set("data", std::move(data.value()));
   }
+
   CallJS("cr.ui.Oobe.showScreen", std::move(screen_params));
 }
 
-void CoreOobeHandler::ReloadContent(base::Value::Dict dictionary) {
-  CallJS("cr.ui.Oobe.reloadContent", std::move(dictionary));
+void CoreOobeHandler::UpdateOobeConfiguration() {
+  CHECK(ui_init_state_ == UiState::kFullyInitialized);
+  base::Value::Dict configuration = configuration::FilterConfiguration(
+      OobeConfiguration::Get()->configuration(),
+      configuration::ConfigurationHandlerSide::HANDLER_JS);
+  CallJS("cr.ui.Oobe.updateOobeConfiguration", std::move(configuration));
 }
 
-void CoreOobeHandler::HandleInitialized() {
-  VLOG(3) << "CoreOobeHandler::HandleInitialized";
-  GetOobeUI()->InitializeHandlers();
+void CoreOobeHandler::ReloadContent() {
+  CHECK(ui_init_state_ == UiState::kFullyInitialized);
+  base::Value::Dict localized_strings = GetOobeUI()->GetLocalizedStrings();
+  CallJS("cr.ui.Oobe.reloadContent", std::move(localized_strings));
+}
+
+void CoreOobeHandler::ForwardCancel() {
+  CHECK(ui_init_state_ == UiState::kFullyInitialized);
+  CallJS("cr.ui.Oobe.handleCancel");
+}
+
+void CoreOobeHandler::SetTabletModeState(bool tablet_mode_enabled) {
+  CHECK(ui_init_state_ == UiState::kFullyInitialized);
+  CallJS("cr.ui.Oobe.setTabletModeState", tablet_mode_enabled);
+}
+
+void CoreOobeHandler::ToggleSystemInfo() {
+  CallJS("cr.ui.Oobe.toggleSystemInfo");
+}
+
+void CoreOobeHandler::TriggerDown() {
+  CallJS("cr.ui.Oobe.triggerDown");
+}
+
+void CoreOobeHandler::EnableKeyboardFlow() {
+  CallJS("cr.ui.Oobe.enableKeyboardFlow", true);
+}
+
+void CoreOobeHandler::SetShelfHeight(int height) {
+  CallJS("cr.ui.Oobe.setShelfHeight", height);
+}
+
+void CoreOobeHandler::SetOrientation(bool is_horizontal) {
+  CallJS("cr.ui.Oobe.setOrientation", is_horizontal);
+}
+
+void CoreOobeHandler::SetDialogSize(int width, int height) {
+  CallJS("cr.ui.Oobe.setDialogSize", width, height);
+}
+
+void CoreOobeHandler::SetVirtualKeyboardShown(bool shown) {
+  CallJS("cr.ui.Oobe.setVirtualKeyboardShown", shown);
+}
+
+void CoreOobeHandler::SetOsVersionLabelText(const std::string& label_text) {
+  CallJS("cr.ui.Oobe.setLabelText", "version", label_text);
+}
+
+void CoreOobeHandler::SetBluetoothDeviceInfo(
+    const std::string& bluetooth_name) {
+  CallJS("cr.ui.Oobe.setBluetoothDeviceInfo", bluetooth_name);
+}
+
+void CoreOobeHandler::HandleInitializeCoreHandler() {
+  VLOG(3) << "CoreOobeHandler::HandleInitializeCoreHandler";
+  CHECK(ui_init_state_ == UiState::kUninitialized);
+  ui_init_state_ = UiState::kCoreHandlerInitialized;
+  AllowJavascript();
+  GetOobeUI()->GetCoreOobe()->UpdateUiInitState(
+      UiState::kCoreHandlerInitialized);
+}
+
+void CoreOobeHandler::HandleScreenStateInitialize() {
+  VLOG(3) << "CoreOobeHandler::HandleScreenStateInitialize";
+  CHECK(ui_init_state_ == UiState::kCoreHandlerInitialized);
+  ui_init_state_ = UiState::kFullyInitialized;
+  GetOobeUI()->GetCoreOobe()->UpdateUiInitState(UiState::kFullyInitialized);
+}
+
+void CoreOobeHandler::HandleEnableShelfButtons(bool enable) {
+  if (LoginDisplayHost::default_host()) {
+    LoginDisplayHost::default_host()->SetShelfButtonsEnabled(enable);
+  }
 }
 
 void CoreOobeHandler::HandleUpdateCurrentScreen(
@@ -173,95 +196,9 @@
   GetOobeUI()->OnBackdropLoaded();
 }
 
-void CoreOobeHandler::HandleEnableShelfButtons(bool enable) {
-  if (LoginDisplayHost::default_host())
-    LoginDisplayHost::default_host()->SetShelfButtonsEnabled(enable);
-}
-
-void CoreOobeHandler::ForwardCancel() {
-  CallJS("cr.ui.Oobe.handleCancel");
-}
-
-void CoreOobeHandler::OnOSVersionLabelTextUpdated(
-    const std::string& os_version_label_text) {
-  UpdateLabel("version", os_version_label_text);
-}
-
-void CoreOobeHandler::OnEnterpriseInfoUpdated(const std::string& message_text,
-                                              const std::string& asset_id) {
-  // Not relevant in OOBE mode.
-}
-
-void CoreOobeHandler::OnDeviceInfoUpdated(const std::string& bluetooth_name) {
-  CallJS("cr.ui.Oobe.setBluetoothDeviceInfo", bluetooth_name);
-}
-
-ui::EventSink* CoreOobeHandler::GetEventSink() {
-  return Shell::GetPrimaryRootWindow()->GetHost()->GetEventSink();
-}
-
-void CoreOobeHandler::UpdateLabel(const std::string& id,
-                                  const std::string& text) {
-  CallJS("cr.ui.Oobe.setLabelText", id, text);
-}
-
-void CoreOobeHandler::OnTabletModeStarted() {
-  CallJS("cr.ui.Oobe.setTabletModeState", true);
-}
-
-void CoreOobeHandler::OnTabletModeEnded() {
-  CallJS("cr.ui.Oobe.setTabletModeState", false);
-}
-
-void CoreOobeHandler::UpdateClientAreaSize(const gfx::Size& size) {
-  CallJS("cr.ui.Oobe.setShelfHeight", ShelfConfig::Get()->shelf_size());
-
-  const gfx::Size display_size =
-      display::Screen::GetScreen()->GetPrimaryDisplay().size();
-  const bool is_horizontal = display_size.width() > display_size.height();
-  CallJS("cr.ui.Oobe.setOrientation", is_horizontal);
-
-  const gfx::Size dialog_size = CalculateOobeDialogSize(
-      size, ShelfConfig::Get()->shelf_size(), is_horizontal);
-  CallJS("cr.ui.Oobe.setDialogSize", dialog_size.width(), dialog_size.height());
-}
-
-void CoreOobeHandler::ToggleSystemInfo() {
-  CallJS("cr.ui.Oobe.toggleSystemInfo");
-}
-
-void CoreOobeHandler::TriggerDown() {
-  CallJS("cr.ui.Oobe.triggerDown");
-}
-
-void CoreOobeHandler::LaunchHelpApp(int help_topic_id) {
-  HandleLaunchHelpApp(help_topic_id);
-}
-
-void CoreOobeHandler::OnOobeConfigurationChanged() {
-  base::Value::Dict configuration = configuration::FilterConfiguration(
-      OobeConfiguration::Get()->configuration(),
-      configuration::ConfigurationHandlerSide::HANDLER_JS);
-  CallJS("cr.ui.Oobe.updateOobeConfiguration", std::move(configuration));
-}
-
-void CoreOobeHandler::OnKeyboardVisibilityChanged(bool shown) {
-  CallJS("cr.ui.Oobe.setVirtualKeyboardShown", shown);
-}
-
 void CoreOobeHandler::HandleLaunchHelpApp(int help_topic_id) {
-  if (!help_app_.get())
-    help_app_ = new HelpAppLauncher(
-        LoginDisplayHost::default_host()->GetNativeWindow());
-  help_app_->ShowHelpTopic(
-      static_cast<HelpAppLauncher::HelpTopic>(help_topic_id));
-}
-
-void CoreOobeHandler::HandleRaiseTabKeyEvent(bool reverse) {
-  ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_NONE);
-  if (reverse)
-    event.set_flags(ui::EF_SHIFT_DOWN);
-  SendEventToSink(&event);
+  LoginDisplayHost::default_host()->GetOobeUI()->GetCoreOobe()->LaunchHelpApp(
+      help_topic_id);
 }
 
 void CoreOobeHandler::HandleUpdateOobeUIState(int state) {
@@ -271,4 +208,12 @@
   }
 }
 
+void CoreOobeHandler::HandleRaiseTabKeyEvent(bool reverse) {
+  ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_NONE);
+  if (reverse) {
+    event.set_flags(ui::EF_SHIFT_DOWN);
+  }
+  SendEventToSink(&event);
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/login/core_oobe_handler.h b/chrome/browser/ui/webui/ash/login/core_oobe_handler.h
index e6ffe73..ebc6bef 100644
--- a/chrome/browser/ui/webui/ash/login/core_oobe_handler.h
+++ b/chrome/browser/ui/webui/ash/login/core_oobe_handler.h
@@ -9,17 +9,10 @@
 #include <string>
 #include <vector>
 
-#include "ash/public/cpp/tablet_mode_observer.h"
-#include "base/functional/callback.h"
 #include "base/values.h"
-#include "chrome/browser/ash/login/help_app_launcher.h"
 #include "chrome/browser/ash/login/oobe_configuration.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/version_info_updater.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
-#include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
 #include "chrome/browser/ui/webui/ash/login/base_webui_handler.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/events/event_source.h"
 
 namespace ui {
@@ -28,34 +21,49 @@
 
 namespace ash {
 
-class CoreOobeView {
+class CoreOobeView : public base::SupportsWeakPtr<CoreOobeView> {
  public:
+  // Possible Initialization States of the UI
+  enum class UiState {
+    kUninitialized,           // Start of things
+    kCoreHandlerInitialized,  // First oobe.js instruction
+    kFullyInitialized,        // Fully initialized a.k.a 'screenStateInitialize'
+  };
+
   virtual ~CoreOobeView() = default;
 
+  // The following methods must only be called once the UI is fully initialized
+  // (kFullyInitialized). It is the responsibility of the client (CoreOobe) to
+  // ensure that this invariant is met. Otherwise it will CHECK().
   virtual void ShowScreenWithData(const OobeScreenId& screen,
                                   absl::optional<base::Value::Dict> data) = 0;
-  virtual void ReloadContent(base::Value::Dict dictionary) = 0;
-  virtual void UpdateClientAreaSize(const gfx::Size& size) = 0;
+  virtual void UpdateOobeConfiguration() = 0;
+  virtual void ReloadContent() = 0;
+  virtual void ForwardCancel() = 0;
+  virtual void SetTabletModeState(bool tablet_mode_enabled) = 0;
+
+  // The following methods are safe to be called at any |UiState|, although the
+  // existing mechanism inside |BaseWebUIHandler| will defer the calls until
+  // JavaScript is allowed in this handler (kCoreHandlerInitialized)
   virtual void ToggleSystemInfo() = 0;
   virtual void TriggerDown() = 0;
-  virtual void ForwardCancel() = 0;
-  virtual void LaunchHelpApp(int help_topic_id) = 0;
+  virtual void EnableKeyboardFlow() = 0;
+  virtual void SetShelfHeight(int height) = 0;
+  virtual void SetOrientation(bool is_horizontal) = 0;
+  virtual void SetDialogSize(int width, int height) = 0;
+  virtual void SetVirtualKeyboardShown(bool shown) = 0;
+  virtual void SetOsVersionLabelText(const std::string& label_text) = 0;
+  virtual void SetBluetoothDeviceInfo(const std::string& bluetooth_name) = 0;
 };
 
 // The core handler for Javascript messages related to the "oobe" view.
 class CoreOobeHandler : public BaseWebUIHandler,
-                        public VersionInfoUpdater::Delegate,
                         public CoreOobeView,
-                        public ui::EventSource,
-                        public TabletModeObserver,
-                        public OobeConfiguration::Observer,
-                        public ChromeKeyboardControllerClient::Observer {
+                        public ui::EventSource {
  public:
-  explicit CoreOobeHandler(const std::string& display_type);
-
+  CoreOobeHandler();
   CoreOobeHandler(const CoreOobeHandler&) = delete;
   CoreOobeHandler& operator=(const CoreOobeHandler&) = delete;
-
   ~CoreOobeHandler() override;
 
   // BaseScreenHandler implementation:
@@ -64,64 +72,46 @@
   void DeclareJSCallbacks() override;
   void GetAdditionalParameters(base::Value::Dict* dict) override;
 
-  // VersionInfoUpdater::Delegate implementation:
-  void OnOSVersionLabelTextUpdated(
-      const std::string& os_version_label_text) override;
-  void OnEnterpriseInfoUpdated(const std::string& message_text,
-                               const std::string& asset_id) override;
-  void OnDeviceInfoUpdated(const std::string& bluetooth_name) override;
-  void OnAdbSideloadStatusUpdated(bool enabled) override {}
-
+ private:
   // ui::EventSource implementation:
   ui::EventSink* GetEventSink() override;
 
- private:
-  // CoreOobeView implementation:
+  // ---- BEGIN --- CoreOobeView
   void ShowScreenWithData(const OobeScreenId& screen,
                           absl::optional<base::Value::Dict> data) override;
-  void ReloadContent(base::Value::Dict dictionary) override;
-  // Updates client area size based on the primary screen size.
-  void UpdateClientAreaSize(const gfx::Size& size) override;
+  void UpdateOobeConfiguration() override;
+  void ReloadContent() override;
+  void ForwardCancel() override;
+  void SetTabletModeState(bool tablet_mode_enabled) override;
+  // Calls above will CHECK() if not |kFullyInitialized|
+  // Calls below are safe at any moment
   void ToggleSystemInfo() override;
   void TriggerDown() override;
-  // Forwards the cancel accelerator value to the shown screen.
-  void ForwardCancel() override;
-  void LaunchHelpApp(int help_topic_id) override;
+  void EnableKeyboardFlow() override;
+  void SetShelfHeight(int height) override;
+  void SetOrientation(bool is_horizontal) override;
+  void SetDialogSize(int width, int height) override;
+  void SetVirtualKeyboardShown(bool shown) override;
+  void SetOsVersionLabelText(const std::string& label_text) override;
+  void SetBluetoothDeviceInfo(const std::string& bluetooth_name) override;
+  // ---- END --- CoreOobeView
 
-  // TabletModeObserver:
-  void OnTabletModeStarted() override;
-  void OnTabletModeEnded() override;
-
-  // OobeConfiguration::Observer:
-  void OnOobeConfigurationChanged() override;
-
-  // ChromeKeyboardControllerClient::Observer:
-  void OnKeyboardVisibilityChanged(bool visible) override;
-
-  // Handlers for JS WebUI messages.
+  // ---- Handlers for JS WebUI messages.
+  void HandleInitializeCoreHandler();
+  void HandleScreenStateInitialize();
   void HandleEnableShelfButtons(bool enable);
-  void HandleInitialized();
   void HandleUpdateCurrentScreen(const std::string& screen);
   void HandleBackdropLoaded();
   void HandleLaunchHelpApp(int help_topic_id);
   // Handles demo mode setup for tests. Accepts 'online' and 'offline' as
   // `demo_config`.
   void HandleUpdateOobeUIState(int state);
-
   // When keyboard_utils.js arrow key down event is reached, raise it
   // to tab/shift-tab event.
   void HandleRaiseTabKeyEvent(bool reverse);
 
-  // Updates label with specified id with specified text.
-  void UpdateLabel(const std::string& id, const std::string& text);
-
-  // Updates when version info is changed.
-  VersionInfoUpdater version_info_updater_{this};
-
-  // Help application used for help dialogs.
-  scoped_refptr<HelpAppLauncher> help_app_;
-
-  bool is_oobe_display_ = false;
+  // Initialization state that is kept in sync with |CoreOobe|.
+  UiState ui_init_state_ = UiState::kUninitialized;
 };
 
 }  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc b/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc
index 8e7e2da..40f7c93 100644
--- a/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc
@@ -8,20 +8,19 @@
 
 #include "base/values.h"
 #include "chrome/browser/ash/login/screens/locale_switch_screen.h"
+#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 
 namespace ash {
 
-LocaleSwitchScreenHandler::LocaleSwitchScreenHandler(
-    CoreOobeView* core_oobe_view)
-    : BaseScreenHandler(kScreenId), core_oobe_view_(core_oobe_view) {}
+LocaleSwitchScreenHandler::LocaleSwitchScreenHandler()
+    : BaseScreenHandler(kScreenId) {}
 
 LocaleSwitchScreenHandler::~LocaleSwitchScreenHandler() = default;
 
 void LocaleSwitchScreenHandler::UpdateStrings() {
-  base::Value::Dict localized_strings = GetOobeUI()->GetLocalizedStrings();
-  core_oobe_view_->ReloadContent(std::move(localized_strings));
+  GetOobeUI()->GetCoreOobe()->ReloadContent();
 }
 
 void LocaleSwitchScreenHandler::DeclareLocalizedValues(
diff --git a/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.h b/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.h
index d86dcde..e2c2da48 100644
--- a/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.h
+++ b/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.h
@@ -31,7 +31,7 @@
  public:
   using TView = LocaleSwitchView;
 
-  explicit LocaleSwitchScreenHandler(CoreOobeView* core_oobe_view);
+  LocaleSwitchScreenHandler();
   ~LocaleSwitchScreenHandler() override;
 
   // LocaleSwitchView:
@@ -40,9 +40,6 @@
   // BaseScreenHandler:
   void DeclareLocalizedValues(
       ::login::LocalizedValuesBuilder* builder) override;
-
- private:
-  raw_ptr<CoreOobeView> core_oobe_view_;
 };
 
 }  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.cc b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
index d54ea79..33e5aa5 100644
--- a/chrome/browser/ui/webui/ash/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
@@ -50,6 +50,7 @@
 #include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/choobe_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h"
 #include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_setup_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/debug/debug_overlay_handler.h"
@@ -371,7 +372,7 @@
   AddScreenHandler(std::make_unique<UpdateScreenHandler>());
 
   if (display_type_ == kOobeDisplay) {
-    AddScreenHandler(std::make_unique<WelcomeScreenHandler>(core_handler_));
+    AddScreenHandler(std::make_unique<WelcomeScreenHandler>());
 
     AddScreenHandler(std::make_unique<DemoPreferencesScreenHandler>());
 
@@ -404,7 +405,7 @@
 
   AddScreenHandler(std::make_unique<EnrollmentScreenHandler>());
 
-  AddScreenHandler(std::make_unique<LocaleSwitchScreenHandler>(core_handler_));
+  AddScreenHandler(std::make_unique<LocaleSwitchScreenHandler>());
 
   AddScreenHandler(std::make_unique<LacrosDataMigrationScreenHandler>());
 
@@ -615,10 +616,11 @@
   LOG(WARNING) << "OobeUI created";
   display_type_ = GetDisplayType(url);
 
-  auto core_handler = std::make_unique<CoreOobeHandler>(display_type_);
-  core_handler_ = core_handler.get();
-
-  AddWebUIHandler(std::move(core_handler));
+  auto core_oobe_handler = std::make_unique<CoreOobeHandler>();
+  core_handler_ = core_oobe_handler.get();
+  core_oobe_ =
+      std::make_unique<CoreOobe>(display_type_, core_oobe_handler->AsWeakPtr());
+  web_ui->AddMessageHandler(std::move(core_oobe_handler));
 
   ConfigureOobeDisplay();
 
@@ -675,8 +677,8 @@
       "worker-src blob: chrome://resources 'self';");
 }
 
-CoreOobeView* OobeUI::GetCoreOobeView() {
-  return core_handler_;
+CoreOobe* OobeUI::GetCoreOobe() {
+  return core_oobe_.get();
 }
 
 ErrorScreen* OobeUI::GetErrorScreen() {
@@ -685,8 +687,10 @@
 
 base::Value::Dict OobeUI::GetLocalizedStrings() {
   base::Value::Dict localized_strings;
+  core_handler_->GetLocalizedStrings(&localized_strings);
   for (BaseWebUIHandler* handler : webui_handlers_)
     handler->GetLocalizedStrings(&localized_strings);
+
   const std::string& app_locale = g_browser_process->GetApplicationLocale();
   webui::SetLoadTimeDataDefaults(app_locale, &localized_strings);
   localized_strings.Set("app_locale", app_locale);
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.h b/chrome/browser/ui/webui/ash/login/oobe_ui.h
index 94e6795e..072a4669 100644
--- a/chrome/browser/ui/webui/ash/login/oobe_ui.h
+++ b/chrome/browser/ui/webui/ash/login/oobe_ui.h
@@ -16,6 +16,7 @@
 #include "base/observer_list.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
+#include "chrome/browser/ash/login/screens/core_oobe.h"
 #include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h"
 #include "chrome/common/webui_url_constants.h"
@@ -88,7 +89,7 @@
 
   ~OobeUI() override;
 
-  CoreOobeView* GetCoreOobeView();
+  CoreOobe* GetCoreOobe();
   ErrorScreen* GetErrorScreen();
 
   // Collects localized strings from the owned handlers.
@@ -207,6 +208,7 @@
 
   // Reference to CoreOobeHandler that handles common requests of Oobe page.
   raw_ptr<CoreOobeHandler, ExperimentalAsh> core_handler_ = nullptr;
+  std::unique_ptr<CoreOobe> core_oobe_;
 
   std::vector<BaseWebUIHandler*> webui_handlers_;       // Non-owning pointers.
   std::vector<BaseWebUIHandler*> webui_only_handlers_;  // Non-owning pointers.
diff --git a/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc b/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc
index 6d6d1b5..af961d6 100644
--- a/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc
@@ -53,10 +53,7 @@
 
 // WelcomeScreenHandler, public: -----------------------------------------------
 
-WelcomeScreenHandler::WelcomeScreenHandler(CoreOobeView* core_oobe_view)
-    : BaseScreenHandler(kScreenId), core_oobe_view_(core_oobe_view) {
-  DCHECK(core_oobe_view_);
-}
+WelcomeScreenHandler::WelcomeScreenHandler() : BaseScreenHandler(kScreenId) {}
 
 WelcomeScreenHandler::~WelcomeScreenHandler() = default;
 
@@ -80,8 +77,7 @@
 
 void WelcomeScreenHandler::SetLanguageList(base::Value::List language_list) {
   language_list_ = std::move(language_list);
-  base::Value::Dict localized_strings = GetOobeUI()->GetLocalizedStrings();
-  core_oobe_view_->ReloadContent(std::move(localized_strings));
+  GetOobeUI()->GetCoreOobe()->ReloadContent();
 }
 
 void WelcomeScreenHandler::SetInputMethodId(
diff --git a/chrome/browser/ui/webui/ash/login/welcome_screen_handler.h b/chrome/browser/ui/webui/ash/login/welcome_screen_handler.h
index d0ab99d..c4d9507 100644
--- a/chrome/browser/ui/webui/ash/login/welcome_screen_handler.h
+++ b/chrome/browser/ui/webui/ash/login/welcome_screen_handler.h
@@ -15,7 +15,6 @@
 
 namespace ash {
 
-class CoreOobeView;
 class WelcomeScreen;
 
 // Interface for WelcomeScreenHandler.
@@ -65,7 +64,7 @@
  public:
   using TView = WelcomeView;
 
-  explicit WelcomeScreenHandler(CoreOobeView* core_oobe_view);
+  WelcomeScreenHandler();
 
   WelcomeScreenHandler(const WelcomeScreenHandler&) = delete;
   WelcomeScreenHandler& operator=(const WelcomeScreenHandler&) = delete;
@@ -97,8 +96,6 @@
 
   // Returns available timezones.
   static base::Value::List GetTimezoneList();
-
-  const raw_ptr<CoreOobeView> core_oobe_view_;
 };
 
 }  // namespace ash