Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
41 changes: 41 additions & 0 deletions docs/validation/code-oss-css-touch-action-799.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Code OSS touch-action and contact pointer implementation (#799)

## Product trigger

Unchanged Code OSS authors 52 `touch-action` declarations across 27 files on
editors, tabs, split views, sliders, terminal, and Chat surfaces. WebScene
previously accepted only legacy mouse input and did not retain a native contact
gesture policy.

## Implemented slice

- Adds generated native/managed CSS identity and CSSOM for non-inherited
`touch-action`: `auto`, `none`, `manipulation`, `pan-x`, `pan-y`, and the
two-axis combination, including CSS-wide values, mutation, and removal.
- Decodes the versioned AppScene #298 pointer metadata from unused input flag
bits while preserving the 48-byte ABI and legacy primary-mouse behavior.
- Exposes stable 1..15 pointer IDs, mouse/touch/pen identity, primary state,
pointer capture by ID, and terminal `pointercancel` teardown.
- Freezes the intersected ancestor policy at contact start and performs
axis-qualified retained scrolling without synthetic mouse events.
- Uses a fixed 16-entry gesture array. CSS mutation is interaction-only, adds
no layout invalidation, document scan, scene node, timer, browser shell, or
Code OSS patch.

## Authored gates

- `webscene_pointer_input_metadata_tests`: legacy fallback, touch identity,
stable ID, malformed input, and future-version rejection.
- `contracts/css-touch-action.html`: non-inheritance, CSSOM, canonical axis
order, invalid-write preservation, CSS-wide inheritance, and removal.
- Native input contract: frozen `pan-y`, touch/pen event fields, capture,
cancellation, no mouse compatibility events, and `none` suppression.
- `css-touch-action-performance`: 4,096 nodes, 40,960 CSS mutations, 1,000
gestures, no layout passes, bounded scene publication, no animation-frame
leak, and a five-second guardrail.

## Evidence state

Implementation and gates are authored from source review. Per the active fast
merge direction, no build, test, WPT, visual, package, memory, lifecycle, or
performance command was executed. Evidence remains zero until full validation.
2 changes: 1 addition & 1 deletion docs/validation/code-oss-web-api-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Static matches establish source reachability only. Runtime claims require the ev
| `form-data` | yes | blocked | linux-x64, macos-arm64, windows-x64 | WebScene#266 | 0 | FormData and multipart transport support exist in the runtime; bounded cross-platform webview/product evidence has not been accepted. |
| `iframe` | yes | blocked | linux-x64, macos-arm64, windows-x64 | WebScene#253, WebScene#264, WebScene#265, WebScene#266, WebScene#267, WebScene#268 | 2 (12 pass, 0 fail, 0 skipped, 0 unavailable, 12 total) | Recorded evidence covers the earlier sandbox Wave 0 revision only. Current service-worker/resource/nested-frame stacks and packaged Markdown must still be promoted with revision-pinned results. |
| `indexeddb` | yes | blocked | linux-x64, macos-arm64, windows-x64 | WebScene#56, WebScene#227 | 0 | Engine contracts exist, but Code OSS origin durability, quota, failure recovery, and package evidence are incomplete. |
| `media-capture` | yes | blocked | linux-x64, macos-arm64, windows-x64 | WebScene#770, WebScene#773, WebScene#775, WebScene#776, WebScene#777, AppScene#289, WebScene#261 | 0 | MediaDevices, microphone tracks, analyser graph delivery, and the narrow Code OSS PCM AudioWorklet are implemented with authored gates. Windows/Linux providers, physical-device and exact-package evidence remain incomplete. |
| `media-capture` | yes | blocked | linux-x64, macos-arm64, windows-x64 | WebScene#770, WebScene#773, WebScene#775, WebScene#776, WebScene#777, AppScene#289, WebScene#261 | 0 | WebScene exposes bounded MediaDevices enumeration, audio getUserMedia host handoff, microphone track lifecycle, graph delivery, and the narrow Code OSS PCM AudioWorklet/MessagePort path with authored but unexecuted gates. AppScene provides the bounded transport and macOS provider; Windows/Linux providers and physical-device evidence remain open. |
| `message-port` | yes | blocked | linux-x64, macos-arm64, windows-x64 | WebScene#81 | 0 | Current implementation evidence has not yet been recorded as an accepted cross-platform run. |
| `observers` | yes | blocked | linux-x64, macos-arm64, windows-x64 | WebScene#227, WebScene#258 | 0 | Observer scheduling, batching, disconnection, memory, and Code OSS panel-churn evidence are incomplete. |
| `performance-timing` | yes | blocked | linux-x64, macos-arm64, windows-x64 | WebScene#227 | 0 | Monotonicity exists but accepted-scenario marks, observers, resource timing, bounds, and package evidence are not reconciled. |
Expand Down
8 changes: 8 additions & 0 deletions experiments/WebScene.NativeEngine.Probe/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ include(CTest)
include(FetchContent)

if(BUILD_TESTING)
add_executable(webscene_pointer_input_metadata_tests
tests/pointer_input_metadata_tests.cpp)
target_compile_features(webscene_pointer_input_metadata_tests PRIVATE cxx_std_20)
target_include_directories(webscene_pointer_input_metadata_tests PRIVATE native)
add_test(NAME webscene_pointer_input_metadata_tests
COMMAND webscene_pointer_input_metadata_tests)
set_tests_properties(webscene_pointer_input_metadata_tests PROPERTIES
LABELS "native;input;pointer;abi;performance" TIMEOUT 10)
add_executable(webscene_semantic_delta_header_tests
tests/semantic_delta_header_tests.c)
target_compile_features(webscene_semantic_delta_header_tests PRIVATE c_std_11)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@
{"id": "isolation", "name": "isolation"},
{"id": "will_change", "name": "will-change", "aliases": ["willchange"]},
{"id": "text_wrap", "name": "text-wrap", "aliases": ["textwrap"]},
{"id": "caret_color", "name": "caret-color", "aliases": ["caretcolor"]}
{"id": "caret_color", "name": "caret-color", "aliases": ["caretcolor"]},
{"id": "touch_action", "name": "touch-action", "aliases": ["touchaction"]}
],
"nativeStorageOnlyProperties": [
"accent-color", "align-content", "animation-direction", "animation-fill-mode", "animation-play-state",
Expand All @@ -181,7 +182,7 @@
"justify_content", "box_sizing", "vertical_align", "grid_auto_flow", "border_collapse", "table_layout", "border_style", "background_repeat",
"visibility", "pointer_events", "text_anchor", "font_smoothing", "text_align", "text_transform", "white_space", "word_break", "overflow_wrap", "list_style_position",
"list_style_type", "scrollbar_width", "container", "container_type", "content_visibility", "object_fit", "user_select",
"overscroll_behavior", "overscroll_behavior_x", "overscroll_behavior_y", "isolation", "will_change", "text_wrap"
"overscroll_behavior", "overscroll_behavior_x", "overscroll_behavior_y", "isolation", "will_change", "text_wrap", "touch_action"
],
"componentList": [
"container_name", "background_position", "background_size", "transition", "transition_property", "transition_duration",
Expand Down Expand Up @@ -225,7 +226,7 @@
"outline", "outline_width", "outline_color", "animation", "animation_name", "animation_duration", "animation_delay",
"animation_timing_function", "animation_iteration_count", "background_repeat", "background_position", "background_size",
"font", "text_transform", "word_break", "overflow_wrap", "list_style", "list_style_position", "list_style_type",
"object_fit", "object_position", "user_select", "overscroll_behavior", "overscroll_behavior_x", "overscroll_behavior_y", "isolation", "will_change", "text_wrap", "caret_color"
"object_fit", "object_position", "user_select", "overscroll_behavior", "overscroll_behavior_x", "overscroll_behavior_y", "isolation", "will_change", "text_wrap", "caret_color", "touch_action"
],
"managedKnownProperties": [
"align-content", "align-items", "align-self", "all", "background", "background-color",
Expand All @@ -247,7 +248,7 @@
"top", "transform", "visibility", "white-space", "word-break", "overflow-wrap", "width", "word-spacing", "z-index",
"outline", "outline-color", "outline-offset", "outline-style", "outline-width",
"grid-template-areas", "aspect-ratio", "object-fit", "object-position", "user-select",
"overscroll-behavior", "overscroll-behavior-x", "overscroll-behavior-y", "isolation", "will-change", "text-wrap", "caret-color"
"overscroll-behavior", "overscroll-behavior-x", "overscroll-behavior-y", "isolation", "will-change", "text-wrap", "caret-color", "touch-action"
],
"supportedPropertyExtras": [
"animation", "animation-delay", "animation-direction", "animation-duration", "animation-fill-mode",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ inline constexpr std::array native_typed_property_identity_catalog{
native_typed_property_identity{"textwrap", css_property_id::text_wrap},
native_typed_property_identity{"caret-color", css_property_id::caret_color},
native_typed_property_identity{"caretcolor", css_property_id::caret_color},
native_typed_property_identity{"touch-action", css_property_id::touch_action},
native_typed_property_identity{"touchaction", css_property_id::touch_action},
};

inline constexpr std::array<std::string_view, 62> native_storage_only_property_catalog{
Expand Down Expand Up @@ -465,6 +467,7 @@ inline constexpr std::array native_modeled_property_mask_catalog{
false,
false,
false,
false,
};

inline constexpr bool generated_property_has_modeled_mask(
Expand Down Expand Up @@ -673,6 +676,7 @@ inline constexpr std::array native_property_grammar_catalog{
native_property_grammar::keyword,
native_property_grammar::keyword,
native_property_grammar::complex,
native_property_grammar::keyword,
};

inline constexpr native_property_grammar generated_property_grammar(
Expand Down Expand Up @@ -919,5 +923,7 @@ inline css_property_id generated_property_id_lowercase(std::string_view name) no
if (name == "textwrap") return css_property_id::text_wrap;
if (name == "caret-color") return css_property_id::caret_color;
if (name == "caretcolor") return css_property_id::caret_color;
if (name == "touch-action") return css_property_id::touch_action;
if (name == "touchaction") return css_property_id::touch_action;
return css_property_id::unknown;
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,4 @@ isolation,
will_change,
text_wrap,
caret_color,
touch_action,
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ inline constexpr std::array cssom_supported_property_catalog{
cssom_supported_property_metadata{"text-transform", "textTransform"},
cssom_supported_property_metadata{"text-wrap", "textWrap"},
cssom_supported_property_metadata{"top", "top"},
cssom_supported_property_metadata{"touch-action", "touchAction"},
cssom_supported_property_metadata{"transform", "transform"},
cssom_supported_property_metadata{"transform-origin", "transformOrigin"},
cssom_supported_property_metadata{"transition", "transition"},
Expand All @@ -263,4 +264,4 @@ inline constexpr std::array cssom_supported_property_catalog{
cssom_supported_property_metadata{"zoom", "zoom"},
};

inline constexpr auto cssom_style_template_property_accessor_count = 468U;
inline constexpr auto cssom_style_template_property_accessor_count = 470U;
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ void apply_resolved_declaration(native_document& document,dom_node& node,
} else if (css::apply_replaced_value(node,name,value,decision)) {
} else if (css::apply_user_select_value(node,name,value,decision)) {
} else if (css::apply_overscroll_behavior_value(node,name,value,decision)) {
} else if (css::apply_touch_action_value(node,name,value,decision)) {
} else if (css::apply_isolation_value(node,name,value,decision)) {
} else if (css::apply_will_change_value(node,name,value,decision)) {
} else if (css::apply_text_value(node,name,value,decision,is_inline)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ inline void reset_cascaded_style(dom_node& node,
textual->will_change.clear();
textual->overscroll_x = overscroll_behavior::automatic;
textual->overscroll_y = overscroll_behavior::automatic;
textual->touch_action_value = touch_action::automatic;
}
if ((node.style.inline_property_mask & inline_font_weight) == 0U) node.style.font_weight = 0;
node.style.isolation_stacking_context = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,66 @@
#include <utility>

namespace webscene_native::css {
inline std::string_view serialize_touch_action(touch_action value) noexcept
{
switch (value) {
case touch_action::none: return "none";
case touch_action::manipulation: return "manipulation";
case touch_action::pan_x: return "pan-x";
case touch_action::pan_y: return "pan-y";
case touch_action::pan_x_y: return "pan-x pan-y";
default: return "auto";
}
}

inline uint8_t touch_action_axes(touch_action value) noexcept
{
switch (value) {
case touch_action::none: return 0U;
case touch_action::pan_x: return 1U;
case touch_action::pan_y: return 2U;
default: return 3U;
}
}

inline std::optional<touch_action> parse_touch_action(
std::string_view value) noexcept
{
if (value == "auto") return touch_action::automatic;
if (value == "none") return touch_action::none;
if (value == "manipulation") return touch_action::manipulation;
if (value == "pan-x") return touch_action::pan_x;
if (value == "pan-y") return touch_action::pan_y;
if (value == "pan-x pan-y" || value == "pan-y pan-x")
return touch_action::pan_x_y;
return std::nullopt;
}

template<typename Decision>
bool apply_touch_action_value(dom_node& node,const std::string& name,
const std::string& raw_value,Decision& decision)
{
if (canonical_property_name(name) != "touch-action") return false;
auto value = ascii_lower(trim_value(raw_value));
if (value == "inherit") {
node.style.mutable_textual().touch_action_value = node.parent == nullptr
? touch_action::automatic
: node.parent->style.textual().touch_action_value;
return true;
}
if (value == "initial" || value == "unset" || value == "revert"
|| value == "revert-layer") value = "auto";
const auto parsed = parse_touch_action(value);
if (!parsed.has_value()) {
decision.classification = "invalid-authoring";
return true;
}
if (*parsed == touch_action::automatic && !node.style.has_textual_data())
return true;
node.style.mutable_textual().touch_action_value = *parsed;
return true;
}

template<typename Decision>
bool apply_user_select_value(dom_node& node,const std::string& name,
const std::string& raw_value,Decision& decision)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ inline void apply_all_unset(
if (has_inline({"overscroll-behavior", "overscroll-behavior-y"})) {
reset.mutable_textual().overscroll_y = previous.textual().overscroll_y;
}
if (has_inline({"touch-action"})) {
reset.mutable_textual().touch_action_value =
previous.textual().touch_action_value;
}
if (has_inline({"isolation"})) {
reset.isolation_stacking_context = previous.isolation_stacking_context;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ inline constexpr auto specified_property_samples = std::to_array<specified_prope
{"will-change","transform, opacity"},
{"text-wrap","nowrap"},
{"caret-color","#123456"},
{"touch-action","pan-y"},
{"width","12px"},{"height","20%"},{"min-width","1rem"},{"min-height","2px"},
{"max-width","none"},{"max-height","fit-content"},
{"left","1px"},{"top","2px"},{"right","3px"},{"bottom","4px"},{"inset","1px 2px 3px 4px"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,15 @@ enum class overscroll_behavior : uint8_t {
none
};

enum class touch_action : uint8_t {
automatic,
none,
manipulation,
pan_x,
pan_y,
pan_x_y
};

enum class float_mode : uint8_t {
none,
left,
Expand Down Expand Up @@ -812,6 +821,9 @@ struct node_style final {
// walks exhausted scroll containers, so keep both axes in cold style.
overscroll_behavior overscroll_x{overscroll_behavior::automatic};
overscroll_behavior overscroll_y{overscroll_behavior::automatic};
// Native gesture admission is consulted only for active touch/pen
// contacts, so keep the non-inherited policy in cold style storage.
touch_action touch_action_value{touch_action::automatic};
std::string contain_value;
std::string container_type{"normal"};
std::string container_name{"none"};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ typedef enum webscene_input_kind {
WEBSCENE_INPUT_TEXT = 9,
// A discrete host-surface exit, not a mousemove. Coordinates may still be
// the last known position inside the surface. Does not cancel capture.
WEBSCENE_INPUT_POINTER_LEAVE = 10
WEBSCENE_INPUT_POINTER_LEAVE = 10,
// Terminal contact cancellation. Older consumers ignore this additive
// kind; current consumers retire capture and gesture state without click.
WEBSCENE_INPUT_POINTER_CANCEL = 17
} webscene_input_kind;

typedef enum webscene_cursor_kind {
Expand Down Expand Up @@ -478,9 +481,26 @@ enum {
// target-offset animator. Unclassified legacy input remains immediate.
WEBSCENE_INPUT_WHEEL_PRECISE = 1U << 20U,
WEBSCENE_INPUT_WHEEL_NATIVE_MOMENTUM = 1U << 21U,
WEBSCENE_INPUT_WHEEL_DISCRETE = 1U << 22U
WEBSCENE_INPUT_WHEEL_DISCRETE = 1U << 22U,
// Binary-compatible pointer metadata. Version zero is a legacy primary
// mouse at ID 1. Version one occupies bits 23..31; consumers must reject
// unknown nonzero versions rather than reinterpret their payload.
WEBSCENE_INPUT_POINTER_METADATA_VERSION_SHIFT = 23U,
WEBSCENE_INPUT_POINTER_METADATA_VERSION_MASK = 3U << 23U,
WEBSCENE_INPUT_POINTER_METADATA_VERSION_1 = 1U << 23U,
WEBSCENE_INPUT_POINTER_DEVICE_SHIFT = 25U,
WEBSCENE_INPUT_POINTER_DEVICE_MASK = 3U << 25U,
WEBSCENE_INPUT_POINTER_PRIMARY = 1U << 27U,
WEBSCENE_INPUT_POINTER_ID_SHIFT = 28U,
WEBSCENE_INPUT_POINTER_ID_MASK = 15U << 28U
};

typedef enum webscene_pointer_device_kind {
WEBSCENE_POINTER_DEVICE_MOUSE = 0,
WEBSCENE_POINTER_DEVICE_TOUCH = 1,
WEBSCENE_POINTER_DEVICE_PEN = 2
} webscene_pointer_device_kind;

typedef struct webscene_input_event {
uint32_t kind;
uint32_t flags;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
case WEBSCENE_INPUT_POINTER_DOWN:
case WEBSCENE_INPUT_POINTER_UP:
case WEBSCENE_INPUT_POINTER_LEAVE:
case WEBSCENE_INPUT_POINTER_CANCEL:
pointer_x_ = event.x;
pointer_y_ = event.y;
#if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8)
Expand Down
Loading
Loading