diff --git a/cobalt/configuration/BUILD.gn b/cobalt/configuration/BUILD.gn new file mode 100644 index 000000000000..a69321a90170 --- /dev/null +++ b/cobalt/configuration/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright 2025 The Cobalt Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +static_library("configuration") { + sources = [ + "configuration.cc", + "configuration.h", + ] + deps = [ + "//base", + "//starboard:starboard_headers_only", + ] +} diff --git a/cobalt/configuration/configuration.cc b/cobalt/configuration/configuration.cc new file mode 100644 index 000000000000..286b823eaac9 --- /dev/null +++ b/cobalt/configuration/configuration.cc @@ -0,0 +1,54 @@ +// Copyright 2025 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cobalt/configuration/configuration.h" + +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "starboard/system.h" + +namespace cobalt { +namespace configuration { + +// static +Configuration* Configuration::GetInstance() { + return base::Singleton>::get(); +} + +Configuration::Configuration() { + configuration_api_ = static_cast( + SbSystemGetExtension(kCobaltExtensionConfigurationName)); + if (configuration_api_) { + // Verify it's the extension needed. + if (strcmp(configuration_api_->name, kCobaltExtensionConfigurationName) != + 0 || + configuration_api_->version < 1) { + LOG(WARNING) << "Not using supplied cobalt configuration extension: " + << "'" << configuration_api_->name << "' (" + << configuration_api_->version << ")"; + configuration_api_ = nullptr; + } + } +} + +const char* Configuration::CobaltUserOnExitStrategy() { + if (configuration_api_) { + return configuration_api_->CobaltUserOnExitStrategy(); + } + return "stop"; +} + +} // namespace configuration +} // namespace cobalt diff --git a/cobalt/configuration/configuration.h b/cobalt/configuration/configuration.h new file mode 100644 index 000000000000..eea7ed8e9d9c --- /dev/null +++ b/cobalt/configuration/configuration.h @@ -0,0 +1,53 @@ +// Copyright 2025 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COBALT_CONFIGURATION_CONFIGURATION_H_ +#define COBALT_CONFIGURATION_CONFIGURATION_H_ + +#include "starboard/extension/configuration.h" + +namespace base { +template +struct DefaultSingletonTraits; +} + +namespace cobalt { +namespace configuration { + +// The Configuration changes certain Cobalt features as specified by the +// platform. This class picks up values set in the +// CobaltConfigurationExtensionApi if it is implemented by the platform and +// will otherwise use default configurations. +class Configuration { + public: + // The Configuration is a singleton initialized on the first call of + // GetInstance(). Calls to this function will return a pointer to the same + // instance as was previously initialized. + static Configuration* GetInstance(); + + const char* CobaltUserOnExitStrategy(); + + private: + Configuration(); + Configuration(const Configuration&) = delete; + Configuration& operator=(const Configuration&) = delete; + + friend struct base::DefaultSingletonTraits; + const CobaltExtensionConfigurationApi* configuration_api_; +}; + +} // namespace configuration +} // namespace cobalt + +#endif // COBALT_CONFIGURATION_CONFIGURATION_H_ diff --git a/cobalt/renderer/BUILD.gn b/cobalt/renderer/BUILD.gn index 76e29c6174e5..422a1cc43d79 100644 --- a/cobalt/renderer/BUILD.gn +++ b/cobalt/renderer/BUILD.gn @@ -16,9 +16,12 @@ source_set("renderer") { sources = [ "cobalt_content_renderer_client.cc", "cobalt_content_renderer_client.h", + "cobalt_render_frame_observer.cc", + "cobalt_render_frame_observer.h", ] deps = [ + "//cobalt/configuration", "//cobalt/media/audio:webaudio", "//components/cdm/renderer", "//components/js_injection/renderer:renderer", @@ -27,6 +30,7 @@ source_set("renderer") { "//content/public/common", "//content/public/renderer", "//content/test:content_test_mojo_bindings", + "//gin", "//media/mojo:buildflags", "//starboard:starboard_headers_only", "//v8", diff --git a/cobalt/renderer/cobalt_content_renderer_client.cc b/cobalt/renderer/cobalt_content_renderer_client.cc index 8a2a27a37bf6..01c1f8ccfbab 100644 --- a/cobalt/renderer/cobalt_content_renderer_client.cc +++ b/cobalt/renderer/cobalt_content_renderer_client.cc @@ -14,6 +14,7 @@ #include "base/strings/string_number_conversions.h" #include "base/task/single_thread_task_runner.h" #include "cobalt/common/shell_switches.h" +#include "cobalt/renderer/cobalt_render_frame_observer.h" #include "components/cdm/renderer/external_clear_key_key_system_info.h" #include "components/network_hints/renderer/web_prescient_networking_impl.h" #include "components/web_cache/renderer/web_cache_impl.h" @@ -107,6 +108,8 @@ void CobaltContentRendererClient::ExposeInterfacesToBrowser( void CobaltContentRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { new js_injection::JsCommunication(render_frame); + CobaltRenderFrameObserver* render_frame_observer = + new CobaltRenderFrameObserver(render_frame); } void CobaltContentRendererClient::PrepareErrorPage( diff --git a/cobalt/renderer/cobalt_render_frame_observer.cc b/cobalt/renderer/cobalt_render_frame_observer.cc new file mode 100644 index 000000000000..946fc14bcd39 --- /dev/null +++ b/cobalt/renderer/cobalt_render_frame_observer.cc @@ -0,0 +1,116 @@ +// Copyright 2025 The Cobalt Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cobalt/renderer/cobalt_render_frame_observer.h" + +#include "cobalt/configuration/configuration.h" +#include "content/public/common/isolated_world_ids.h" +#include "content/public/renderer/chrome_object_extensions_utils.h" +#include "content/public/renderer/render_frame.h" +#include "gin/converter.h" +#include "starboard/system.h" +#include "third_party/blink/public/web/blink.h" +#include "third_party/blink/public/web/web_local_frame.h" +#include "v8/include/v8.h" + +namespace cobalt { + +namespace { + +enum UserOnExitStrategy { + kUserOnExitStrategyClose, + kUserOnExitStrategyMinimize, + kUserOnExitStrategyNoExit, +}; + +uint32_t GetUserOnExitStrategy() { + // Convert from the Cobalt gyp setting variable's enum options to the H5VCC + // interface enum options. + std::string exit_strategy_str( + configuration::Configuration::GetInstance()->CobaltUserOnExitStrategy()); + if (exit_strategy_str == "stop") { + return static_cast(kUserOnExitStrategyClose); + } else if (exit_strategy_str == "suspend") { +#if SB_IS(EVERGREEN) + // Note: The status string used here must be synced with the + // ComponentState::kUpdated status string defined in updater_module.cc. + if (updater_->GetUpdateStatus() == "Update installed, pending restart") { + return static_cast(kUserOnExitStrategyClose); + } +#endif + return static_cast(kUserOnExitStrategyMinimize); + } else if (exit_strategy_str == "noexit") { + return static_cast(kUserOnExitStrategyNoExit); + } else { + NOTREACHED() << "Unknown exit strategy."; + return static_cast(kUserOnExitStrategyClose); + } +} + +void WindowClose(const v8::FunctionCallbackInfo& info) { + SbSystemRequestStop(0); +} + +void WindowMinimize(const v8::FunctionCallbackInfo& info) { + SbSystemRequestConceal(); +} + +void InstallCloseAndMinimize(content::RenderFrame* render_frame) { + if (!render_frame) { + return; + } + + v8::Isolate* isolate = blink::MainThreadIsolate(); + v8::HandleScope handle_scope(isolate); + v8::Local context = + render_frame->GetWebFrame()->MainWorldScriptContext(); + if (context.IsEmpty()) { + return; + } + + v8::Context::Scope context_scope(context); + v8::Local window = context->Global(); + // v8::Local window = + // content::GetOrCreateObject(isolate, context, "window"); + window + ->Set(context, gin::StringToSymbol(isolate, "minimize"), + v8::Function::New(context, WindowMinimize).ToLocalChecked()) + .Check(); + window + ->Set(context, gin::StringToSymbol(isolate, "close"), + v8::Function::New(context, WindowClose).ToLocalChecked()) + .Check(); + v8::Local h5vcc = + content::GetOrCreateObject(isolate, context, "h5vcc"); + v8::Local h5vcc_system = + content::GetOrCreateObject(isolate, context, h5vcc, "system"); + // Set exit strategy to minimize. + + h5vcc_system->Set(context, gin::StringToSymbol(isolate, "userOnExitStrategy"), + v8::Integer::New(isolate, 1)); +} + +} // namespace + +CobaltRenderFrameObserver::CobaltRenderFrameObserver( + content::RenderFrame* render_frame) + : content::RenderFrameObserver(render_frame) {} + +void CobaltRenderFrameObserver::DidCreateScriptContext( + v8::Local v8_context, + int32_t world_id) { + // Only install on the main world context of the main frame. + if (!render_frame() || !render_frame()->IsMainFrame() || + world_id != content::ISOLATED_WORLD_ID_GLOBAL) { + return; + } + + InstallCloseAndMinimize(render_frame()); +} + +void CobaltRenderFrameObserver::OnDestruct() { + delete this; +} + +} // namespace cobalt diff --git a/cobalt/renderer/cobalt_render_frame_observer.h b/cobalt/renderer/cobalt_render_frame_observer.h new file mode 100644 index 000000000000..77cdd963d2c1 --- /dev/null +++ b/cobalt/renderer/cobalt_render_frame_observer.h @@ -0,0 +1,30 @@ +// Copyright 2025 The Cobalt Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COBALT_RENDERER_COBALT_RENDERER_FRAME_OBSERVER_H_ +#define COBALT_RENDERER_COBALT_RENDERER_FRAME_OBSERVER_H_ + +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_frame_observer.h" +#include "v8/include/v8.h" + +namespace cobalt { + +class CobaltRenderFrameObserver : public content::RenderFrameObserver { + public: + explicit CobaltRenderFrameObserver(content::RenderFrame* render_frame); + + CobaltRenderFrameObserver(const CobaltRenderFrameObserver&) = delete; + CobaltRenderFrameObserver& operator=(const CobaltRenderFrameObserver&) = + delete; + + private: + void DidCreateScriptContext(v8::Local v8_context, + int32_t world_id) override; + void OnDestruct() override; +}; + +} // namespace cobalt + +#endif // COBALT_RENDERER_COBALT_RENDERER_FRAME_OBSERVER_H_