[fuchsia] Skeleton impl of webengine/PushMessagingServiceImpl

This change is fairly straightforward, it creates the implementation of
PushMessagingService in fuchsia_web/webengine/browser, but leaves
everything unimplemented. The only thing slightly more is to create the
gcm::GCMDriver. It follows how ios does it.
https://source.chromium.org/chromium/chromium/src/+/main:ios/chrome/browser/application_context/model/application_context_impl.mm;l=730;drc=285461c1e4957f186a9273ae4db8796ea3c35c88

To avoid interfering with the current build, the feature is disabled via
a gn arg.

So far, it's surprising that the binary size isn't impacted at all which
means the new deps were originally imported indirectly.

Bug: 424479300
Change-Id: I3fb5bc4148160b219a25bef0b5f07c3e033b6c63
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6651220
Commit-Queue: Zijie He <zijiehe@google.com>
Reviewed-by: Sergey Ulanov <sergeyu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1497087}
diff --git a/fuchsia_web/webengine/BUILD.gn b/fuchsia_web/webengine/BUILD.gn
index 56c9a79e..04167890 100644
--- a/fuchsia_web/webengine/BUILD.gn
+++ b/fuchsia_web/webengine/BUILD.gn
@@ -17,6 +17,11 @@
 # Only allow use by targets in this directory unless explicitly specified.
 visibility = [ ":*" ]
 
+declare_args() {
+  # A temporary gn arg to enable the push messaging apis.
+  enable_push_messaging_api = false
+}
+
 config("web_engine_implementation") {
   defines = [ "WEB_ENGINE_IMPLEMENTATION" ]
 }
@@ -182,6 +187,7 @@
     ":web_engine_export_from_implementation",
     "//base",
     "//base:base_static",
+    "//build:branding_buildflags",
     "//build:chromecast_buildflags",
     "//components/cdm/renderer",
     "//components/client_hints/browser:in_memory",
@@ -191,6 +197,7 @@
     "//components/favicon/content",
     "//components/favicon/core",
     "//components/fuchsia_component_support",
+    "//components/gcm_driver",
     "//components/keyed_service/content",
     "//components/media_control/browser",
     "//components/media_control/renderer",
@@ -317,6 +324,8 @@
     "browser/navigation_policy_handler.h",
     "browser/navigation_policy_throttle.cc",
     "browser/navigation_policy_throttle.h",
+    "browser/push_messaging_service_impl.cc",
+    "browser/push_messaging_service_impl.h",
     "browser/theme_manager.cc",
     "browser/theme_manager.h",
     "browser/trace_event.h",
@@ -373,9 +382,9 @@
     ]
   }
 
-  # TODO(crbug.com/40031409): Fix code that adds exit-time destructors and
-  # enable the diagnostic by removing this line.
-  configs += [ "//build/config/compiler:no_exit_time_destructors" ]
+  if (enable_push_messaging_api) {
+    defines = [ "WEB_ENGINE_ENABLE_PUSH_MESSAGING_API" ]
+  }
 }
 
 source_set("context_provider") {
diff --git a/fuchsia_web/webengine/browser/DEPS b/fuchsia_web/webengine/browser/DEPS
index ed3c3313..db52119f 100644
--- a/fuchsia_web/webengine/browser/DEPS
+++ b/fuchsia_web/webengine/browser/DEPS
@@ -5,6 +5,7 @@
   "+components/client_hints/browser",
   "+components/favicon/content",
   "+components/favicon/core",
+  "+components/gcm_driver",
   "+components/keyed_service/content",
   "+components/keyed_service/core",
   "+components/on_load_script_injector/browser",
diff --git a/fuchsia_web/webengine/browser/push_messaging_service_impl.cc b/fuchsia_web/webengine/browser/push_messaging_service_impl.cc
new file mode 100644
index 0000000..a823760
--- /dev/null
+++ b/fuchsia_web/webengine/browser/push_messaging_service_impl.cc
@@ -0,0 +1,148 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fuchsia_web/webengine/browser/push_messaging_service_impl.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/check.h"
+#include "base/files/file_path.h"
+#include "base/fuchsia/file_utils.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "build/branding_buildflags.h"
+#include "components/gcm_driver/gcm_client_factory.h"
+#include "components/gcm_driver/gcm_desktop_utils.h"
+#include "components/gcm_driver/gcm_driver_constants.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+
+PushMessagingServiceImpl::PushMessagingServiceImpl(
+    content::BrowserContext& parent_context)
+    : parent_context_(parent_context) {}
+
+PushMessagingServiceImpl::~PushMessagingServiceImpl() = default;
+
+// PushMessagingService implementations.
+void PushMessagingServiceImpl::SubscribeFromDocument(
+    const GURL& requesting_origin,
+    int64_t service_worker_registration_id,
+    int render_process_id,
+    int render_frame_id,
+    blink::mojom::PushSubscriptionOptionsPtr options,
+    bool user_gesture,
+    RegisterCallback callback) {}
+
+void PushMessagingServiceImpl::SubscribeFromWorker(
+    const GURL& requesting_origin,
+    int64_t service_worker_registration_id,
+    int render_process_id,
+    blink::mojom::PushSubscriptionOptionsPtr options,
+    RegisterCallback callback) {}
+
+void PushMessagingServiceImpl::GetSubscriptionInfo(
+    const GURL& origin,
+    int64_t service_worker_registration_id,
+    const std::string& sender_id,
+    const std::string& subscription_id,
+    SubscriptionInfoCallback callback) {}
+
+void PushMessagingServiceImpl::Unsubscribe(
+    blink::mojom::PushUnregistrationReason reason,
+    const GURL& requesting_origin,
+    int64_t service_worker_registration_id,
+    const std::string& sender_id,
+    UnregisterCallback callback) {}
+
+bool PushMessagingServiceImpl::SupportNonVisibleMessages() {
+  return false;
+}
+
+void PushMessagingServiceImpl::DidDeleteServiceWorkerRegistration(
+    const GURL& origin,
+    int64_t service_worker_registration_id) {}
+
+void PushMessagingServiceImpl::DidDeleteServiceWorkerDatabase() {}
+
+// GCMAppHandler implementations.
+void PushMessagingServiceImpl::ShutdownHandler() {}
+
+void PushMessagingServiceImpl::OnStoreReset() {}
+
+void PushMessagingServiceImpl::OnMessage(const std::string& app_id,
+                                         const gcm::IncomingMessage& message) {}
+
+void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {}
+
+void PushMessagingServiceImpl::OnSendError(
+    const std::string& app_id,
+    const gcm::GCMClient::SendErrorDetails& send_error_details) {}
+
+void PushMessagingServiceImpl::OnSendAcknowledged(
+    const std::string& app_id,
+    const std::string& message_id) {}
+
+gcm::GCMDriver& PushMessagingServiceImpl::GetGCMDriver() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!gcm_driver_) {
+    // No predefined blocking_task_runner, create one dedicated for gcm_driver.
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
+        base::ThreadPool::CreateSequencedTaskRunner(
+            {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+             base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}));
+
+    gcm_driver_ = gcm::CreateGCMDriverDesktop(
+        std::make_unique<gcm::GCMClientFactory>(), /* prefs = */ nullptr,
+        base::FilePath(base::kPersistedDataDirectoryPath)
+            .Append(gcm_driver::kGCMStoreDirname),
+        base::BindPostTask(
+            base::SequencedTaskRunner::GetCurrentDefault(),
+            base::BindRepeating(
+                &PushMessagingServiceImpl::RequestProxyResolvingSocketFactory,
+                weak_ptr_factory_.GetWeakPtr())),
+        GetSharedURLLoaderFactory(),
+        /* network_connection_tracker = */ nullptr, GetChannel(),
+        GetProductCategoryForSubtypes(),
+        content::BrowserThread::GetTaskRunnerForThread(
+            content::BrowserThread::ID::UI),
+        content::BrowserThread::GetTaskRunnerForThread(
+            content::BrowserThread::ID::IO),
+        blocking_task_runner);
+  }
+  DCHECK(gcm_driver_);
+  return *gcm_driver_.get();
+}
+
+void PushMessagingServiceImpl::RequestProxyResolvingSocketFactory(
+    mojo::PendingReceiver<network::mojom::ProxyResolvingSocketFactory>
+        receiver) {
+  parent_context_.GetDefaultStoragePartition()
+      ->GetNetworkContext()
+      ->CreateProxyResolvingSocketFactory(std::move(receiver));
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+PushMessagingServiceImpl::GetSharedURLLoaderFactory() const {
+  return parent_context_.GetDefaultStoragePartition()
+      ->GetURLLoaderFactoryForBrowserProcess();
+}
+
+version_info::Channel PushMessagingServiceImpl::GetChannel() const {
+  // Only stable version of WebEngine is released to the users.
+  return version_info::Channel::STABLE;
+}
+
+std::string PushMessagingServiceImpl::GetProductCategoryForSubtypes() const {
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  return "com.chrome.fuchsia";
+#else
+  return "org.chromium.fuchsia";
+#endif
+}
diff --git a/fuchsia_web/webengine/browser/push_messaging_service_impl.h b/fuchsia_web/webengine/browser/push_messaging_service_impl.h
new file mode 100644
index 0000000..610b72c
--- /dev/null
+++ b/fuchsia_web/webengine/browser/push_messaging_service_impl.h
@@ -0,0 +1,94 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FUCHSIA_WEB_WEBENGINE_BROWSER_PUSH_MESSAGING_SERVICE_IMPL_H_
+#define FUCHSIA_WEB_WEBENGINE_BROWSER_PUSH_MESSAGING_SERVICE_IMPL_H_
+
+#include <memory>
+
+#include "base/sequence_checker.h"
+#include "base/version_info/channel.h"
+#include "components/gcm_driver/gcm_app_handler.h"
+#include "components/gcm_driver/gcm_driver.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/push_messaging_service.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+class PushMessagingServiceImpl : public content::PushMessagingService,
+                                 public gcm::GCMAppHandler {
+ public:
+  PushMessagingServiceImpl(content::BrowserContext&);
+  ~PushMessagingServiceImpl() override;
+
+  // PushMessagingService implementations.
+  void SubscribeFromDocument(const GURL& requesting_origin,
+                             int64_t service_worker_registration_id,
+                             int render_process_id,
+                             int render_frame_id,
+                             blink::mojom::PushSubscriptionOptionsPtr options,
+                             bool user_gesture,
+                             RegisterCallback callback) override;
+  void SubscribeFromWorker(const GURL& requesting_origin,
+                           int64_t service_worker_registration_id,
+                           int render_process_id,
+                           blink::mojom::PushSubscriptionOptionsPtr options,
+                           RegisterCallback callback) override;
+  void GetSubscriptionInfo(const GURL& origin,
+                           int64_t service_worker_registration_id,
+                           const std::string& sender_id,
+                           const std::string& subscription_id,
+                           SubscriptionInfoCallback callback) override;
+  void Unsubscribe(blink::mojom::PushUnregistrationReason reason,
+                   const GURL& requesting_origin,
+                   int64_t service_worker_registration_id,
+                   const std::string& sender_id,
+                   UnregisterCallback callback) override;
+  bool SupportNonVisibleMessages() override;
+  void DidDeleteServiceWorkerRegistration(
+      const GURL& origin,
+      int64_t service_worker_registration_id) override;
+  void DidDeleteServiceWorkerDatabase() override;
+
+  // GCMAppHandler implementations.
+  void ShutdownHandler() override;
+  void OnStoreReset() override;
+  void OnMessage(const std::string& app_id,
+                 const gcm::IncomingMessage& message) override;
+  void OnMessagesDeleted(const std::string& app_id) override;
+  void OnSendError(
+      const std::string& app_id,
+      const gcm::GCMClient::SendErrorDetails& send_error_details) override;
+  void OnSendAcknowledged(const std::string& app_id,
+                          const std::string& message_id) override;
+
+ private:
+  gcm::GCMDriver& GetGCMDriver();
+
+  void RequestProxyResolvingSocketFactory(
+      mojo::PendingReceiver<network::mojom::ProxyResolvingSocketFactory>
+          receiver);
+
+  scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory()
+      const;
+
+  version_info::Channel GetChannel() const;
+
+  std::string GetProductCategoryForSubtypes() const;
+
+  // Lazy initialized.
+  std::unique_ptr<gcm::GCMDriver> gcm_driver_;
+
+  // Outlive this instance.
+  content::BrowserContext& parent_context_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // Must be the last member variable.
+  base::WeakPtrFactory<PushMessagingServiceImpl> weak_ptr_factory_{this};
+};
+
+#endif  // FUCHSIA_WEB_WEBENGINE_BROWSER_PUSH_MESSAGING_SERVICE_IMPL_H_
diff --git a/fuchsia_web/webengine/browser/web_engine_browser_context.cc b/fuchsia_web/webengine/browser/web_engine_browser_context.cc
index 4077c35..2cdf2083 100644
--- a/fuchsia_web/webengine/browser/web_engine_browser_context.cc
+++ b/fuchsia_web/webengine/browser/web_engine_browser_context.cc
@@ -123,7 +123,11 @@
 
 content::PushMessagingService*
 WebEngineBrowserContext::GetPushMessagingService() {
+#ifdef WEB_ENGINE_ENABLE_PUSH_MESSAGING_API
+  return &push_messaging_service_;
+#else
   return nullptr;
+#endif
 }
 
 content::StorageNotificationService*
@@ -194,7 +198,12 @@
       client_hints_delegate_(network_quality_tracker,
                              IsJavaScriptAllowedCallback(),
                              embedder_support::GetUserAgentMetadata()),
-      reduce_accept_language_delegate_(GetAcceptLanguages()) {
+      reduce_accept_language_delegate_(GetAcceptLanguages())
+#ifdef WEB_ENGINE_ENABLE_PUSH_MESSAGING_API
+      ,
+      push_messaging_service_(*this)
+#endif
+{
   SimpleKeyMap::GetInstance()->Associate(this, &simple_factory_key_);
 
   profile_metrics::SetBrowserProfileType(
diff --git a/fuchsia_web/webengine/browser/web_engine_browser_context.h b/fuchsia_web/webengine/browser/web_engine_browser_context.h
index 9c4c8b8e..0bc2426b 100644
--- a/fuchsia_web/webengine/browser/web_engine_browser_context.h
+++ b/fuchsia_web/webengine/browser/web_engine_browser_context.h
@@ -12,6 +12,7 @@
 #include "components/keyed_service/core/simple_factory_key.h"
 #include "components/reduce_accept_language/browser/in_memory_reduce_accept_language_service.h"
 #include "content/public/browser/browser_context.h"
+#include "fuchsia_web/webengine/browser/push_messaging_service_impl.h"
 #include "fuchsia_web/webengine/browser/web_engine_permission_delegate.h"
 
 class WebEngineNetLogObserver;
@@ -76,6 +77,9 @@
   client_hints::InMemoryClientHintsControllerDelegate client_hints_delegate_;
   reduce_accept_language::InMemoryReduceAcceptLanguageService
       reduce_accept_language_delegate_;
+#ifdef WEB_ENGINE_ENABLE_PUSH_MESSAGING_API
+  PushMessagingServiceImpl push_messaging_service_;
+#endif
 };
 
 #endif  // FUCHSIA_WEB_WEBENGINE_BROWSER_WEB_ENGINE_BROWSER_CONTEXT_H_