Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6530d40
Add native clipboard and window capability bridge
wieslawsoltes Sep 15, 2026
d1c0e6c
Classify desktop host APIs outside legacy interop
wieslawsoltes Sep 15, 2026
adecc4e
Keep oversized URL fixture self contained
wieslawsoltes Sep 15, 2026
b2ff9cb
Use typed native desktop host requests
wieslawsoltes Sep 15, 2026
88c2e59
Migrate embed fallback tests to typed host requests
wieslawsoltes Sep 15, 2026
2c726b4
Keep typed request flags warning-free
wieslawsoltes Sep 15, 2026
259962c
Gate typed clipboard round-trip performance
wieslawsoltes Sep 15, 2026
8746a65
Synchronize typed host request tests with engine work
wieslawsoltes Sep 15, 2026
ec31e13
Document typed desktop capability boundary
wieslawsoltes Sep 15, 2026
af00bd0
Synchronize canvas clipboard host request test
wieslawsoltes Sep 15, 2026
1d0bc8f
Make typed host request tests worker-ordered
wieslawsoltes Sep 15, 2026
9873b60
Expose fullscreen in generated DOM bindings
wieslawsoltes Sep 15, 2026
9dc2eef
Order native fullscreen event reset
wieslawsoltes Sep 15, 2026
5669183
Dispatch native clipboard shortcut events
wieslawsoltes Sep 15, 2026
4984dc3
Document browser clipboard shortcut behavior
wieslawsoltes Sep 15, 2026
bebff63
Expose clipboard shortcut regression context
wieslawsoltes Sep 15, 2026
143d0bc
Trace native clipboard shortcut dispatch
wieslawsoltes Sep 15, 2026
4c47954
Wait for clipboard shortcut input consumption
wieslawsoltes Sep 15, 2026
a37e387
Drain clipboard shortcut input queue frontier
wieslawsoltes Sep 15, 2026
1b0c6c8
Record synchronized clipboard shortcut state
wieslawsoltes Sep 15, 2026
9ab6337
Isolate native clipboard shortcut contracts
wieslawsoltes Sep 15, 2026
6563be8
Wait for isolated clipboard setup
wieslawsoltes Sep 15, 2026
3a321cb
Wait for serialized clipboard representation
wieslawsoltes Sep 15, 2026
4b9a19c
Expose clipboard shortcut event sequence
wieslawsoltes Sep 15, 2026
0a16806
Wait for asynchronous native paste event
wieslawsoltes Sep 15, 2026
4d033cb
Compare serialized clipboard event value once
wieslawsoltes Sep 15, 2026
ec146da
Support user-initiated legacy clipboard commands
wieslawsoltes Sep 15, 2026
502fe1e
Report legacy clipboard command results
wieslawsoltes Sep 15, 2026
24c123a
Synchronize asynchronous clipboard shortcut assertions
wieslawsoltes Sep 15, 2026
d75a8d2
Avoid nested paste microtask checkpoints
wieslawsoltes Sep 15, 2026
66de665
Trace legacy clipboard command completion
wieslawsoltes Sep 15, 2026
453b90c
Use host-realistic native shortcut key codes
wieslawsoltes Sep 15, 2026
59ef9a4
Add native clipboard shutdown stress diagnostics
wieslawsoltes Sep 15, 2026
49c11d0
Release pending host handles before V8 teardown
wieslawsoltes Sep 15, 2026
241b951
Expose clipboard string items to paste consumers
wieslawsoltes Sep 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/native-runtime-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,15 @@ jobs:
--upstream-v8 \
--partition-alloc \
--output "$GITHUB_WORKSPACE/artifacts/nuget-packages"
- name: Upload exact macOS native symbols
if: matrix.rid == 'osx-arm64'
uses: actions/upload-artifact@v4
with:
name: native-symbols-${{ matrix.rid }}-${{ needs.metadata.outputs.package-version }}
path: artifacts/native-engine-runtime-build/**/*.dSYM/**
if-no-files-found: error
compression-level: 9
retention-days: 3
- name: Build, pack, and test Linux runtime
if: matrix.rid == 'linux-x64'
shell: bash
Expand Down
53 changes: 49 additions & 4 deletions docs/code-oss-compatibility.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Code OSS native runtime compatibility

Status: development integration. Code OSS 1.137.0 reaches farther through the
native AppScene/WebScene stack with three runtime-owned compatibility slices,
but the complete workbench/editor smoke is not yet qualified. This work does
not add or depend on Electron, CEF, WKWebView, or a browser process.
Status: development integration. Code OSS 1.137.0's complete native
workbench/editor smoke passes against the development runtime. The typed desktop
capability revision still requires a fresh combined package qualification. This
work does not add or depend on Electron, CEF, WKWebView, or a browser process.

The reference consumer is
[`SceneTech/vscode-demo`](https://github.com/SceneTech/vscode-demo). It starts
Expand Down Expand Up @@ -41,6 +41,37 @@ tests are tracked in
The token-specific bridge must remain outside WebScene until that general
contract exists.

## Native desktop capability boundary

WebScene exposes standard browser behavior instead of an Electron emulation
layer. The versioned `webscene_host_request_v1` ABI carries external HTTP(S)
URLs, clipboard reads/writes, window focus/close/reload, and fullscreen
enter/exit. Hosts lease immutable request memory and release it explicitly.
Clipboard byte payloads therefore avoid base64 and JSON allocation. The older
bounded JSON queue remains available for application messages and compatibility
with earlier hosts.

`navigator.clipboard` supports Promise-based text and typed operations with a
16 MiB representation limit, at most 16 pending completion-bearing operations,
explicit MIME rejection, recent native user activation for reads, and
navigation cancellation. Native Ctrl/Command-C, X, and V input also dispatches
the standard bubbling, cancelable `copy`, `cut`, and `paste` events expected by
browser editors. Copy and cut collect plain-text and HTML representations from
the event before the async host write; paste captures the initiating target and
dispatches after the bounded host read completes. A canceled `keydown` never
reaches this default behavior. Window requests share a bounded queue. Native hosts
can publish focus and fullscreen state back into the active document, which
updates `document.hasFocus()`, fullscreen state, and their standard events.
Scripted close dispatches cancelable `beforeunload` before reaching the host.

The Code OSS `server-web` target does not load Electron main/sandbox entry
points. Its shipped `out/vs` tree has no direct `electron` module import, so a
general Electron shim would add surface area without helping DOM, Monaco,
layout, input, worker, rendering, or scheduling performance. AppScene owns the
operating-system side of the typed ABI. Menus, dialogs, notifications,
secondary windows, power events, and protocol registration remain
evidence-gated capabilities; they are not installed speculatively.

## Open PR interaction

- WebScene #70 changes CSS compilation/cache representation. CSSOM publication
Expand Down Expand Up @@ -78,6 +109,20 @@ regressions without turning temporary hosted-runner load into a flaky result:
| Read an 8 MiB Blob | 3,000 ms | 9 ms | 528 ms | 33 ms |
| Retain 50,000 marks | 1,500 ms | 54 ms | 239 ms | 75 ms |
| Publish 450 stylesheet mutations | 2,000 ms | 19 ms | 71 ms | 23 ms |
| Complete 10,000 one-byte typed clipboard writes | 10,000 ms | pending latest package matrix | pending latest package matrix | pending latest package matrix |
| Drain 10,000 typed window requests | 5,000 ms | pending latest package matrix | pending latest package matrix | pending latest package matrix |
| Apply 10,000 native focus transitions | 1,000 ms | pending latest package matrix | pending latest package matrix | pending latest package matrix |

The clipboard load gate uses batches of 16, reports that pending-request high
water mark, completes every Promise, and verifies an empty queue without timing
sleeps. A separate gate moves the maximum 16 MiB clipboard payload through the
typed ABI within five seconds. Native contracts also cover no-activation reads,
unsupported input, cancellation, queue saturation, stale/double completion,
navigation teardown, close veto, and native-initiated fullscreen changes.
The native shortcut contract additionally verifies byte-exact plain-text and
HTML host writes plus a completed host read delivered through `ClipboardEvent`
shape, covering the path used by Monaco rather than only direct Clipboard API
calls.

Measurements were recorded on 2026-09-15 with Node 25.1.0 on macOS, Node
18.19.1 in Ubuntu 24.04, and Node 24.19.0 in Windows 11. The VM runs used the
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Generated by tools/webidl-v8-bindings/generate.mjs. Do not edit.
// Exposure manifest SHA-256: e0b8a262956eb19c856043f2aca06a86e1f66a198adc4c52bd8ef4879f155c09
// Exposure manifest SHA-256: fdbb9a18487dbd2997d4224fcae8863bb911d901383a14640c4f53087072df97
// Inputs: @webref/idl 3.82.1, webidl2 24.5.0.

enum class generated_dom_interface : uint8_t {
Expand Down Expand Up @@ -3305,6 +3305,12 @@ void install_generated_dom_templates(v8::Local<v8::Context>)
isolate, attach_shadow, v8::Local<v8::Value>(),
generated_Element_signature, 1,
v8::ConstructorBehavior::kThrow));
generated_Element_template->PrototypeTemplate()->Set(
js_string(isolate, "requestFullscreen"),
v8::FunctionTemplate::New(
isolate, element_request_fullscreen, v8::Local<v8::Value>(),
generated_Element_signature, 0,
v8::ConstructorBehavior::kThrow));
element_template.Reset(isolate, generated_Element_template);

auto generated_SVGElement_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,10 @@ struct webscene_engine final {
std::atomic<bool> low_memory_requested_{false};
std::atomic<bool> host_visible_{true};
std::atomic<bool> visibility_changed_{false};
std::atomic<bool> host_focused_{true};
std::atomic<bool> focus_changed_{false};
std::atomic<bool> host_fullscreen_{false};
std::atomic<bool> fullscreen_changed_{false};
std::atomic<uint32_t> preferred_color_scheme_{
WEBSCENE_PREFERRED_COLOR_SCHEME_LIGHT};
std::atomic<bool> preferred_color_scheme_changed_{false};
Expand Down Expand Up @@ -1291,6 +1295,58 @@ size_t webscene_engine_take_host_request(
: engine->take_host_request(destination, destination_capacity);
}

const webscene_host_request_v1*
webscene_engine_take_typed_host_request_v1(webscene_engine* engine)
{
if (engine == nullptr) return nullptr;
auto request = engine->take_typed_host_request();
if (!request) return nullptr;
request->bind();
return &request.release()->view;
}

void webscene_host_request_release_v1(
const webscene_host_request_v1* request)
{
delete reinterpret_cast<const webscene_native::native_host_request*>(request);
}

uint8_t webscene_engine_discard_host_request_v1(webscene_engine* engine)
{
return engine != nullptr && engine->discard_host_request() ? 1U : 0U;
}

uint8_t webscene_engine_complete_host_request_v1(
webscene_engine* engine,
uint64_t request_id,
uint32_t status,
const char* content_type,
const uint8_t* bytes,
size_t byte_count,
const char* error_message)
{
constexpr size_t maximum_clipboard_bytes = 16U * 1024U * 1024U;
if (engine == nullptr || request_id == 0U || status > 2U
|| byte_count > maximum_clipboard_bytes
|| (byte_count != 0U && bytes == nullptr)) {
return 0U;
}
webscene_native::native_host_completion completion;
completion.id = request_id;
completion.status = status;
completion.content_type = content_type == nullptr ? "" : content_type;
completion.error = error_message == nullptr ? "" : error_message;
if (completion.content_type.size() > 256U || completion.error.size() > 4096U)
return 0U;
if (status == 0U) {
if (byte_count != 0U && completion.content_type.empty()) return 0U;
if (byte_count != 0U) completion.bytes.assign(bytes, bytes + byte_count);
} else if (byte_count != 0U) {
return 0U;
}
return engine->complete_host_request(std::move(completion)) ? 1U : 0U;
}

void webscene_engine_configure_diagnostics(
webscene_engine* engine, uint32_t flags,
webscene_diagnostic_available_callback callback, void* user_data)
Expand Down Expand Up @@ -1416,6 +1472,18 @@ uint8_t webscene_engine_set_visible(webscene_engine* engine, uint8_t visible)
return engine != nullptr && engine->set_visible(visible != 0) ? 1U : 0U;
}

uint8_t webscene_engine_set_window_focused_v1(
webscene_engine* engine, uint8_t focused)
{
return engine != nullptr && engine->set_focused(focused != 0) ? 1U : 0U;
}

uint8_t webscene_engine_set_window_fullscreen_v1(
webscene_engine* engine, uint8_t fullscreen)
{
return engine != nullptr && engine->set_fullscreen(fullscreen != 0) ? 1U : 0U;
}

uint8_t webscene_engine_set_preferred_color_scheme(
webscene_engine* engine,
uint32_t preferred_color_scheme)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,15 @@ _webscene_engine_request_scene_checkpoint
_webscene_engine_set_resource_root
_webscene_engine_set_preferred_color_scheme
_webscene_engine_set_visible
_webscene_engine_set_window_focused_v1
_webscene_engine_set_window_fullscreen_v1
_webscene_engine_take_console_message
_webscene_engine_take_diagnostic
_webscene_engine_take_host_request
_webscene_engine_take_typed_host_request_v1
_webscene_host_request_release_v1
_webscene_engine_complete_host_request_v1
_webscene_engine_discard_host_request_v1
_webscene_engine_take_input_dispatch_failure
_webscene_engine_take_invoke_result_v3
_webscene_engine_take_callback_v3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,14 @@ WEBSCENE_API uint8_t webscene_engine_request_low_memory(webscene_engine* engine)
* worker; returning visible before the deadline cancels it.
*/
WEBSCENE_API uint8_t webscene_engine_set_visible(webscene_engine* engine, uint8_t visible);
/* Publishes native key-window focus to document.hasFocus() and standard
* top-level focus/blur events. Repeated values are coalesced. */
WEBSCENE_API uint8_t webscene_engine_set_window_focused_v1(
webscene_engine* engine, uint8_t focused);
/* Synchronizes fullscreen changes initiated by native window controls. Script
* initiated transitions use the typed request/completion path. */
WEBSCENE_API uint8_t webscene_engine_set_window_fullscreen_v1(
webscene_engine* engine, uint8_t fullscreen);
/*
* Updates the host's effective color preference. The worker re-evaluates CSS
* media rules and subsequent Window.matchMedia snapshots against this value.
Expand Down Expand Up @@ -1366,10 +1374,61 @@ WEBSCENE_API uint8_t webscene_engine_complete_file_request_v1(webscene_engine* e
uint64_t request_id, uint32_t status, const webscene_file_data_v1* files,
size_t file_count, const char* error_message);

/* Typed native desktop request ABI. Request memory is immutable and remains
* valid until release. Byte payloads are capped at 16 MiB, strings are UTF-8,
* and at most 16 completion-bearing operations may be pending per document. */
enum {
WEBSCENE_HOST_REQUEST_OPEN_EXTERNAL_URL_V1 = 1,
WEBSCENE_HOST_REQUEST_CLIPBOARD_READ_V1 = 2,
WEBSCENE_HOST_REQUEST_CLIPBOARD_WRITE_V1 = 3,
WEBSCENE_HOST_REQUEST_WINDOW_FOCUS_V1 = 4,
WEBSCENE_HOST_REQUEST_WINDOW_CLOSE_V1 = 5,
WEBSCENE_HOST_REQUEST_WINDOW_RELOAD_V1 = 6,
WEBSCENE_HOST_REQUEST_FULLSCREEN_ENTER_V1 = 7,
WEBSCENE_HOST_REQUEST_FULLSCREEN_EXIT_V1 = 8
};
enum {
WEBSCENE_HOST_REQUEST_CLIPBOARD_REPLACE_V1 = 1U << 0U
};
typedef struct webscene_host_request_v1 {
uint32_t struct_size, version;
uint64_t request_id;
uint32_t kind, flags;
uint64_t target_node_id;
const char* content_type;
const uint8_t* bytes;
size_t byte_count;
const char* url;
} webscene_host_request_v1;
WEBSCENE_API const webscene_host_request_v1*
webscene_engine_take_typed_host_request_v1(webscene_engine* engine);
WEBSCENE_API void webscene_host_request_release_v1(
const webscene_host_request_v1* request);

/* JSON compatibility queue retained for older host integrations and unrelated
* application-defined messages. New desktop capabilities use the typed ABI. */
WEBSCENE_API size_t webscene_engine_take_host_request(
webscene_engine* engine,
char* destination,
size_t destination_capacity);
/* Consumes the oldest JSON compatibility request without allocating its
* payload. Hosts use this after rejecting an oversized item so one malformed
* request cannot permanently block the FIFO. */
WEBSCENE_API uint8_t webscene_engine_discard_host_request_v1(
webscene_engine* engine);
/* Completes a request carrying a numeric requestId from take_host_request.
* status: 0 completed, 1 cancelled, 2 denied/failed. Inputs are copied before
* return. Clipboard data is limited to 16 MiB, content_type to 256 bytes and
* error_message to 4096 bytes. Completion is delivered on the engine worker;
* stale request IDs are safely ignored there. */
WEBSCENE_API uint8_t webscene_engine_complete_host_request_v1(
webscene_engine* engine,
uint64_t request_id,
uint32_t status,
const char* content_type,
const uint8_t* bytes,
size_t byte_count,
const char* error_message);
/*
* Removes one V8 console entry. The UTF-8 payload is `<level>\n<message>`;
* querying with a null/short destination reports the required byte count
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ using script_work_request =
std::variant<
script_request,
webscene_native::native_file_completion,
webscene_native::native_host_completion,
url_request,
interop_evaluate_work_v3,
interop_invoke_work_v3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@
signal_worker();
return true;
}
bool complete_host_request(webscene_native::native_host_completion completion) {
std::lock_guard lock(script_mutex_);
if (script_work_.size() >= 1024) return false;
script_work_.emplace_back(std::move(completion));
signal_worker();
return true;
}
bool discard_host_request() {
#if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8)
std::lock_guard lock(file_runtime_mutex_);
return file_runtime_ready_ && runtime_->discard_host_request();
#else
return false;
#endif
}
void retire_file_runtime() {
#if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8)
std::unique_ptr<webscene_native::v8_dom_runtime> retired;
Expand Down Expand Up @@ -130,6 +145,14 @@
#if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8)
std::lock_guard lock(file_runtime_mutex_);
if (file_runtime_ready_) return runtime_->take_file_request();
#endif
return {};
}
std::unique_ptr<webscene_native::native_host_request>
take_typed_host_request() {
#if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8)
std::lock_guard lock(file_runtime_mutex_);
if (file_runtime_ready_) return runtime_->take_typed_host_request();
#endif
return {};
}
Expand Down Expand Up @@ -215,6 +238,21 @@
return true;
}

bool set_focused(bool focused)
{
host_focused_.store(focused, std::memory_order_release);
focus_changed_.store(true, std::memory_order_release);
signal_worker();
return true;
}
bool set_fullscreen(bool fullscreen)
{
host_fullscreen_.store(fullscreen, std::memory_order_release);
fullscreen_changed_.store(true, std::memory_order_release);
signal_worker();
return true;
}

bool set_preferred_color_scheme(uint32_t preferred_color_scheme)
{
if (preferred_color_scheme != WEBSCENE_PREFERRED_COLOR_SCHEME_LIGHT
Expand Down
Loading
Loading