Autoroll from main to 27.lts#10785
Conversation
…l, and Web NFC Refer to original PR: #10488 This change systematically eliminates unused peripheral hardware modules from the Cobalt Android APK to reduce binary size by ~295 KB. Specific changes: 1. third_party/blink/renderer/modules/BUILD.gn: - Excluded bluetooth, hid, webusb, serial, and nfc from sub_modules in Cobalt builds. 2. third_party/blink/renderer/bindings/modules/v8/BUILD.gn: - Excluded auto-generated V8 wrapper sources for bluetooth, hid, usb, serial, and ndef. 3. third_party/blink/renderer/modules/service_worker/BUILD.gn & service_worker_global_scope.cc: - Gated hid and webusb dependencies and event handler lookups behind BUILDFLAG(IS_COBALT). 4. third_party/blink/renderer/modules/cobalt_modules_stubs.cc: - Implemented dummy V8 WrapperTypeInfo stubs and C++ accessors for Bluetooth, USB, HID, and Serial to satisfy link-time supplement references. More savings might be achieved by removing the //services and //device code for these APIs; those couldn't be removed earlier due to the transitive chain of deps (blink --> content --> services --> device), but after this CL it should be possible. Bug: 498257436 (cherry picked from commit d04472f)
… MediaCodecDecoder audio loop Refer to original PR: #10747 This PR optimize this by moving `dequeue_output_results` to the function scope and calling `.clear()` at the start of the loop. This allows the vector to reuse its underlying memory capacity across iterations, eliminating costly C++ heap allocation/deallocation churn on the audio playback path. Issue: 519300681 (cherry picked from commit 9bb9ab3)
Refer to original PR: #10729 This PR decouples Cobalt's dependency on WebGPU and Dawn, completing the final step of the pruning plan. This is Step 3 of 3 (following WebNN #10644 and WebXR #10649) to achieve a binary size reduction. Pruning WebGPU/Dawn shows an additional binary size decrease of ~245.7 KB ( 92,831,100 down to 92,585,340 bytes), bringing the total cumulative savings across all 3 steps to ~800 KB on Android builds. #### Key Changes: 1. Build Graph Pruning (GN Subtraction): • Subtracted webgpu module from sub_modules in `third_party/blink/renderer/modules/BUILD.gn`. • Gated WebGPU/Dawn dependencies behind !is_cobalt in platform and command buffer targets: • `third_party/blink/renderer/platform/BUILD.gn` • `third_party/blink/renderer/platform/graphics/canvas/BUILD.gn` • `gpu/command_buffer/service/BUILD.gn` 2. Bindings Filtering: • Consolidated exclude patterns in `third_party/blink/renderer/bindings/bindings.gni` under `cobalt_webgpu_exclude_patterns` (covering WebGPU, WebXR, and WebNN wrappers). • Excluded generated WebGPU V8 bindings in `third_party/blink/renderer/bindings/modules/v8/BUILD.gn` to prevent their compilation into libv8.a . 3. Lightweight C++ & Dawn Stubbing: • Dawn Proc Table Stubs: Introduced `cobalt_webgpu_stubs.cc` to stub out the massive Dawn C-API proc table. This satisfies the V8/Blink link-time requirements cleanly without pulling in the actual heavy Dawn library. • WebGPU Blink Stubs: Appended WebGPU V8 wrappers and class stub entry points to `cobalt_modules_stubs.cc`. • Safe Core Isolation: Gated WebGPU logic in core rendering structures behind !BUILDFLAG(IS_COBALT) (e.g. in `base_rendering_context_2d.cc` and `canvas_2d_recorder_context.cc `) while preserving member layouts to guarantee zero Chromium merge conflicts. Note: This PR does not affect Skia/Canvas 2D GPU hardware acceleration, it only removes the ability for WebGPU to access/render into a 2D GPU Canvas. Confirmed that `is_accelerated=1` in `canvas_resource_provider.cc` at runtime. Bug: 512589073 (cherry picked from commit a25741f)
…evices in MinimumSupport Refer to original PR: #10580 Fix mistakenly added VP9 codec string in PR #9479 Removed the incorrect codec string "vp09.00.01.00.22", which was mistakenly introduced in PR #9479 as a minimum requirement. According to the [YouTube Living Room Devices Hardware Certification Requirements (2026)](https://developers.google.com/youtube/devices/living-room/requirements/2026/hardware-certification#media-decoding-video-decoders-reqs), FHD & 4K devices must support: * Codec: VP9 Profile 0, 8-bit decoder, Level 4.1 * Expected string: codecs="vp09.00.41.08" The previously added string "vp09.00.01.00.22" does not match the certification requirements and should not be included in the minimum support list. Bug: 479068555 Ref: https://www.webmproject.org/vp9/mp4/#:~:text=The%20string%20codecs%3D%22vp09.00.41.08%22%20in%20this%20case%20would%20represent%20VP9%20profile%200%2C%20level%204.1%2C%208%2Dbit%20YUV%20content%20with%204%3A2%3A0%20chroma%20subsampling Co-authored-by: Jonas Tsai <jonastsai@google.com> (cherry picked from commit 3dd97be)
Refer to original PR: #10671 Add several UMA histograms to track the status and lifecycle of Finch experiment configurations. These metrics help monitor if configurations are successfully loaded, when safe mode is activated, and the reasons for discarding configurations, such as expiration or version rollbacks. Furthermore, these changes include metrics to track when configurations are written to storage and the time intervals between these writes to improve observability of the experiment system performance. Bug: 515520976 (cherry picked from commit 67377e0)
…TLS mapping and Allocator Observers' Refer to original PR: #10725 Reapply 'Implement base MemoryContext TLS mapping and Allocator Observers' (#10723) with fixes for Android compile failures. **Android pthread_getname_np Warning Fix:** The `__builtin_available(android 26, *)` guard is applied directly inside `base/memory/cobalt_memory_attribution_observer.cc`. Bug: 498702469 (cherry picked from commit 474ba99)
… for Starboard Refer to original PR: #10749 Reuse `scratch_frame_buf_` capacity in `MP4StreamParser::EnqueueSample` for Starboard to avoid per-frame heap allocations during video playback. Reusing this is possible on Starboard because the frame data is copied into the media pool rather than moved (which would release/deallocate the vector's backing memory). Issue: 518914404 (cherry picked from commit b6506ad)
Refer to original PR: #10743 `_Exit`, not to be confused with the existing `exit`, is leaking from NPLB because it is a used by gmock. This adds the `_Exit` symbol by implementing a simple wrapper that ensures the exit status retains its meaning across platforms. Bug: 477257357 --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> (cherry picked from commit a5c52bd)
Refer to original PR: #10683 This removes symbols leaks of dlopen and dlsym, which we do not support in Starboard. The source of the undefined symbols in C27 is the tflite library, which is primarily a dependency of AI features in upstream Chromium that Cobalt does not use. For example, language detection and auto translation. This PR refactors build rules to exclude tflite from the Cobalt build, including by removing the Blink language detection library, and is ironically mostly generated by Gemini. As a bonus, binary size is reduced by ~450K Bug: 500419612 (cherry picked from commit 75170f6)
…e JNI Refer to original PR: #10745 Frequent recreation of AudioTrackBridge during loop playback (e.g. YouTube Shorts) was causing a Java heap memory leak and eventual OutOfMemoryError. The leak occurred because the raw JNI local references returned by `env->NewFloatArray` and `env->NewByteArray` in `AudioTrackBridge::Create` were passed directly to `ScopedJavaGlobalRef::Reset` without being wrapped in `ScopedJavaLocalRef`. Because the player worker thread is persistent and reused across loops, these local references accumulated over time, keeping the associated Java arrays (512KB each) alive on the Java heap. This change fixes the leak by directly assigning temporary `ScopedJavaLocalRef` wrappers (holding the newly allocated JNI arrays) to the `ScopedJavaGlobalRef` `j_audio_data_`. This ensures that the local reference is automatically and explicitly deleted when the temporary wrapper goes out of scope at the end of the assignment statement, while the global reference is correctly initialized. Issue: 517536842 (cherry picked from commit df8c019)
Refer to original PR: #10471 Cobalt does not require WebRtc processing as it adds additional latency for the microphone path. Normally this is used for echo cancellation and automatic gain control when handling two way calls, which we don't need or support. Bug: 483713292 (cherry picked from commit b99b4b3)
…ed test executables Refer to original PR: #10767 - Extend `test_targets_json` action to output `test_executables_json`. - Modify `on_host_tests` to accept `test_executables_json` instead of hardcoded parsing. - Update `main.yaml` to pipe the output correctly. Bug: 431780245 (cherry picked from commit 8e03b09)
Refer to original PR: #10754 Introduce a ScopedFeatureList utility class to allow tests to toggle Starboard features. This helper provides a mechanism to override feature states within a specific scope, ensuring that global configuration is restored once the test finishes. The FeatureList class is updated with methods to set and clear testing overrides, and the Android media capabilities tests are updated to use this new functionality for verifying codec rejection logic. Issue: 512137801 (cherry picked from commit 7b6cdc9)
Refer to original PR: #10416 This fixes missing visibilityChange events on resume caused by early focus signaling. How it works: The fix blocks focus at the narrowest point in the browser process, in `WebContentsImpl::Focus()`, while waiting for the reveal acknowledgment from the web application. It also ignores OS-initiated focus events in `PlatformWindowStarboard::ProcessFocusEvent` and defers all focus-triggering actions until `OnPageVisibilityVisible`. This commit fixes the missing `visibilitychange` event in the YTS test by deferring the focus event until AFTER the web application has acknowledged that it received the visibility change signal. Previously, the focus event was delivered too early, causing race conditions. By ignoring early focus events and only triggering focus after the web app calls `PageVisibilityVisible` via Mojo, we ensure the strict sequence: [Unfreeze -> Reveal -> Focus], which satisfies the YTS test requirements. Detailed changes: 1. Blink Side (Detecting Visibility and Sending Signal) - third_party/blink/renderer/modules/cobalt/h5vcc_runtime/h_5_vcc_runtime.h & .cc: Inherits from PageVisibilityObserver to listen for visibility changes in Blink. Implements PageVisibilityChanged() to send a Mojo message (PageVisibilityVisible) to the browser process when the page becomes visible on resume. 2. Mojo Interface - cobalt/browser/h5vcc_runtime/public/mojom/h5vcc_runtime.mojom: Adds the PageVisibilityVisible() method to the H5vccRuntime interface to bridge the signal from Blink to the browser. 3. Browser Side (Receiving Signal and State Management) - cobalt/browser/h5vcc_runtime/h5vcc_runtime_impl.h & .cc: Receives the PageVisibilityVisible Mojo call from Blink. Calls OnPageVisibilityVisible on ShellPlatformDelegate. - cobalt/shell/browser/shell_platform_delegate.h & .cc: Maintains the waiting_for_reveal_ack_ state. In OnReveal(), sets this flag to true and informs Starboard. In OnPageVisibilityVisible(), clears the flag and triggers all the deferred actions (showing windows and applying focus). 4. Browser Side (Deferring Actions and Ignoring Early Focus) - cobalt/shell/browser/shell_platform_data_aura.cc: Constructor defers calling host_->window()->Show() if waiting_for_reveal_ack() is true. - cobalt/shell/browser/shell_platform_delegate_aura.cc: SetContents() defers showing the content window if waiting for reveal ACK. RevealShell() calls Show with kInactive to prevent automatic focus stealing on resume. - content/browser/web_contents/web_contents_impl.cc: Ignores calls to Focus() if waiting_for_reveal_ack() is true, preventing early programmatic focus. - ui/ozone/platform/starboard/platform_window_starboard.h & .cc: Maintains a static waiting_for_reveal_ack_ flag accessible by both Cobalt and Ozone. ProcessFocusEvent() and Activate() return early if this flag is true, ignoring OS-level focus events during resume. - cobalt/shell/browser/shell.h & .cc: Modifies Focus() and ActivateContents() to respect waiting_for_reveal_ack(). Adds ShouldFocusPageAfterCrash to also respect this flag. Bug: 477170017 (cherry picked from commit 61ea9b3)
Refer to original PR: #10776 Ensure the MIME type mapping for a source buffer ID is cleared when the ID is removed from the demuxer. This prevents the demuxer from holding onto stale state in the Starboard media implementation, maintaining consistency across internal maps when source buffers are detached. Bug: 519741453 (cherry picked from commit 91650b1)
There was a problem hiding this comment.
Code Review
This pull request introduces a multi-process lifecycle coordination mechanism for Cobalt, adding a browser-side CobaltLifecycleManager and a renderer-side CobaltLifecycleController to handle asynchronous lifecycle ACKs. It also adds memory context tracking, suspends hang watching during application freeze, and optimizes media parsing and binary size by stubbing out unused Blink modules. The review feedback highlights critical security and performance issues: multiple Use-After-Free (UAF) vulnerabilities in CobaltLifecycleController and CobaltLifecycleManager due to unsafe object references in asynchronous tasks, a performance bottleneck in the memory observer's thread-name lookup, and potential undefined behavior in the MP4 stream parser when accessing an empty vector.
| const char* GetCurrentOSThreadName() { | ||
| static thread_local char tls_thread_name[32] = {0}; | ||
| if (tls_thread_name[0] == '\0') { | ||
| #if BUILDFLAG(IS_ANDROID) | ||
| if (__builtin_available(android 26, *)) { | ||
| if (pthread_getname_np(pthread_self(), tls_thread_name, sizeof(tls_thread_name)) != 0) { | ||
| tls_thread_name[0] = '\0'; | ||
| return nullptr; | ||
| } | ||
| } else { | ||
| return nullptr; | ||
| } | ||
| #else | ||
| if (pthread_getname_np(pthread_self(), tls_thread_name, sizeof(tls_thread_name)) != 0) { | ||
| tls_thread_name[0] = '\0'; | ||
| return nullptr; | ||
| } | ||
| #endif | ||
| } | ||
| return tls_thread_name; | ||
| } |
There was a problem hiding this comment.
In GetCurrentOSThreadName(), checking tls_thread_name[0] == '\0' to determine if the thread name has been initialized is highly problematic. If a thread does not have a name set, pthread_getname_np will succeed but write an empty string (i.e., tls_thread_name[0] remains \0). This causes pthread_getname_np (a system call) to be invoked on every single allocation on that thread, introducing a massive performance bottleneck in a very hot path.
Additionally, on Android versions older than API 26, __builtin_available will be evaluated on every allocation.
We should use a separate thread_local bool to track whether the thread name has been initialized, ensuring pthread_getname_np is called at most once per thread.
const char* GetCurrentOSThreadName() {
static thread_local char tls_thread_name[32] = {0};
static thread_local bool tls_thread_name_initialized = false;
if (!tls_thread_name_initialized) {
tls_thread_name_initialized = true;
#if BUILDFLAG(IS_ANDROID)
if (__builtin_available(android 26, *)) {
if (pthread_getname_np(pthread_self(), tls_thread_name, sizeof(tls_thread_name)) != 0) {
tls_thread_name[0] = '\0';
}
}
#else
if (pthread_getname_np(pthread_self(), tls_thread_name, sizeof(tls_thread_name)) != 0) {
tls_thread_name[0] = '\0';
}
#endif
}
return tls_thread_name[0] != '\0' ? tls_thread_name : nullptr;
}| void CobaltLifecycleManager::OnMojoDisconnect() { | ||
| CHECK_CALLED_ON_VALID_THREAD(thread_checker_); | ||
| FrameContext context = receivers_.current_context(); | ||
| if (context.web_contents) { | ||
| UnregisterFrame(context.web_contents, context.frame); | ||
| } | ||
| } |
There was a problem hiding this comment.
In OnMojoDisconnect(), accessing context.web_contents directly is unsafe. When a WebContents is destroyed, OnWebContentsDestroyed() is called synchronously, which cleans up the maps but does not synchronously destroy the Mojo receivers. The Mojo disconnect callbacks are dispatched asynchronously on the message loop. By the time OnMojoDisconnect() runs, context.web_contents is a dangling pointer, leading to a Use-After-Free (UAF) vulnerability when used as a key in UnregisterFrame().
We should verify that the web_contents pointer is still valid by checking if it exists in trackers_ before calling UnregisterFrame().
| void CobaltLifecycleManager::OnMojoDisconnect() { | |
| CHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| FrameContext context = receivers_.current_context(); | |
| if (context.web_contents) { | |
| UnregisterFrame(context.web_contents, context.frame); | |
| } | |
| } | |
| void CobaltLifecycleManager::OnMojoDisconnect() { | |
| CHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| FrameContext context = receivers_.current_context(); | |
| if (context.web_contents && trackers_.find(context.web_contents) != trackers_.end()) { | |
| UnregisterFrame(context.web_contents, context.frame); | |
| } | |
| } |
| } | ||
| if (GetPage()) { | ||
| NotifyObserver(base::BindOnce( | ||
| [](HeapMojoRemote<cobalt::mojom::blink::CobaltLifecycleObserver>& | ||
| observer, | ||
| bool visible) { | ||
| if (observer.is_bound()) { | ||
| observer->OnPageVisibilityChanged(visible); | ||
| } | ||
| }, | ||
| std::ref(remote_observer_), visible)); |
There was a problem hiding this comment.
Passing std::ref(remote_observer_) to base::BindOnce inside NotifyObserver is extremely dangerous. CobaltLifecycleController is a garbage-collected object. If it is garbage-collected before the posted task runs, the reference to remote_observer_ will be dangling, leading to a Use-After-Free (UAF) crash when the lambda executes.
We should capture this via WrapWeakPersistent(this) to safely manage the lifetime of the garbage-collected object.
NotifyObserver(base::BindOnce(
[](CobaltLifecycleController* controller, bool visible) {
if (controller && controller->remote_observer_.is_bound()) {
controller->remote_observer_->OnPageVisibilityChanged(visible);
}
},
WrapWeakPersistent(this), visible));| if (GetPage()) { | ||
| bool is_focused = GetPage()->GetFocusController().IsFocused(); | ||
| if (is_focused) { | ||
| NotifyObserver(base::BindOnce( | ||
| [](HeapMojoRemote<cobalt::mojom::blink::CobaltLifecycleObserver>& | ||
| observer) { | ||
| if (observer.is_bound()) { | ||
| observer->OnPageFocused(); | ||
| } | ||
| }, | ||
| std::ref(remote_observer_))); | ||
| } else { | ||
| NotifyObserver(base::BindOnce( | ||
| [](HeapMojoRemote<cobalt::mojom::blink::CobaltLifecycleObserver>& | ||
| observer) { | ||
| if (observer.is_bound()) { | ||
| observer->OnPageBlurred(); | ||
| } | ||
| }, | ||
| std::ref(remote_observer_))); | ||
| } | ||
| } |
There was a problem hiding this comment.
Passing std::ref(remote_observer_) to base::BindOnce inside NotifyObserver is extremely dangerous. CobaltLifecycleController is a garbage-collected object. If it is garbage-collected before the posted task runs, the reference to remote_observer_ will be dangling, leading to a Use-After-Free (UAF) crash when the lambda executes.
We should capture this via WrapWeakPersistent(this) to safely manage the lifetime of the garbage-collected object.
bool is_focused = GetPage() && GetPage()->GetFocusController().IsFocused();
NotifyObserver(base::BindOnce(
[](CobaltLifecycleController* controller, bool is_focused) {
if (controller && controller->remote_observer_.is_bound()) {
if (is_focused) {
controller->remote_observer_->OnPageFocused();
} else {
controller->remote_observer_->OnPageBlurred();
}
}
},
WrapWeakPersistent(this), is_focused));| if (state == mojom::blink::FrameLifecycleState::kRunning) { | ||
| if (remote_observer_.is_bound()) { | ||
| NotifyObserver(base::BindOnce( | ||
| [](HeapMojoRemote<cobalt::mojom::blink::CobaltLifecycleObserver>& | ||
| observer) { | ||
| if (observer.is_bound()) { | ||
| observer->OnPageResumed(); | ||
| } | ||
| }, | ||
| std::ref(remote_observer_))); | ||
| } | ||
| } |
There was a problem hiding this comment.
Passing std::ref(remote_observer_) to base::BindOnce inside NotifyObserver is extremely dangerous. CobaltLifecycleController is a garbage-collected object. If it is garbage-collected before the posted task runs, the reference to remote_observer_ will be dangling, leading to a Use-After-Free (UAF) crash when the lambda executes.
We should capture this via WrapWeakPersistent(this) to safely manage the lifetime of the garbage-collected object.
if (state == mojom::blink::FrameLifecycleState::kRunning) {
NotifyObserver(base::BindOnce(
[](CobaltLifecycleController* controller) {
if (controller && controller->remote_observer_.is_bound()) {
controller->remote_observer_->OnPageResumed();
}
},
WrapWeakPersistent(this)));
}| stream_buf = StreamParserBuffer::CopyFrom( | ||
| base::span<const uint8_t>{&frame_buf[0], frame_buf.size()}, | ||
| is_keyframe, buffer_type, runs_->track_id()); |
There was a problem hiding this comment.
Using &frame_buf[0] on a potentially empty vector is undefined behavior in C++ and can cause crashes or assertions in hardened STL configurations.
We should use base::span<const uint8_t>{frame_buf} directly, which is safe and idiomatic.
stream_buf = StreamParserBuffer::CopyFrom(
base::span<const uint8_t>{frame_buf},
is_keyframe, buffer_type, runs_->track_id());
|
Automated cherry-pick roll to 27.lts.
Original pull requests:
sigaltstackandsched_getcpu#10738_Exit#10743getpriorityto allowed symbols list #10768