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
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@

std::array<queued_input_event, input_capacity> values_{};
std::mutex producer_mutex_;
alignas(64) std::atomic<uint32_t> write_{0};

Check warning on line 145 in experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.cpp

View workflow job for this annotation

GitHub Actions / Build win-x64

'`anonymous-namespace'::input_ring': structure was padded due to alignment specifier
alignas(64) std::atomic<uint32_t> read_{0};

Check warning on line 146 in experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.cpp

View workflow job for this annotation

GitHub Actions / Build win-x64

'`anonymous-namespace'::input_ring': structure was padded due to alignment specifier
};

struct canvas_layer_version final {
Expand Down Expand Up @@ -2303,7 +2303,7 @@
uint32_t webscene_scene_gpu_image_count_v3(const webscene_scene_view_v3* view)
{
if (!view || view->struct_size < sizeof(*view) || view->scene_version!=3 || !view->lease_token) return 0;
return static_cast<const scene_lease_v3*>(view->lease_token)->cpu.value->gpu_images.size();

Check warning on line 2306 in experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.cpp

View workflow job for this annotation

GitHub Actions / Build win-x64

'return': conversion from 'size_t' to 'uint32_t', possible loss of data
}
webscene_scene_acquire_status webscene_gpu_image_retain_v3(
const webscene_gpu_image_lease_v3* image,webscene_gpu_image_lease_v3** result)
Expand Down Expand Up @@ -2637,6 +2637,46 @@
const webscene_download_request_v1* request) {
delete reinterpret_cast<const webscene_native::native_download_request*>(request);
}
const webscene_outbound_drag_request_v1*
webscene_engine_take_outbound_drag_request_v1(webscene_engine* engine) {
if (!engine) return nullptr;
auto request = engine->take_outbound_drag_request();
if (!request) return nullptr;
request->bind();
return &request.release()->view;
}
void webscene_outbound_drag_request_release_v1(
const webscene_outbound_drag_request_v1* request) {
delete reinterpret_cast<
const webscene_native::native_outbound_drag_request*>(request);
}
uint8_t webscene_engine_complete_outbound_drag_v1(
webscene_engine* engine,
const webscene_outbound_drag_completion_v1* completion) {
constexpr uint32_t allowed_operations =
WEBSCENE_OUTBOUND_DRAG_OPERATION_COPY_V1
| WEBSCENE_OUTBOUND_DRAG_OPERATION_LINK_V1
| WEBSCENE_OUTBOUND_DRAG_OPERATION_MOVE_V1;
constexpr uint32_t allowed_modifiers =
WEBSCENE_INPUT_POINTER_MODIFIER_SHIFT
| WEBSCENE_INPUT_POINTER_MODIFIER_CONTROL
| WEBSCENE_INPUT_POINTER_MODIFIER_ALT
| WEBSCENE_INPUT_POINTER_MODIFIER_META;
if (engine == nullptr || completion == nullptr
|| completion->struct_size < sizeof(*completion)
|| completion->version != 1U || completion->request_id == 0U
|| completion->document_generation == 0U
|| completion->status < WEBSCENE_OUTBOUND_DRAG_COMPLETED_V1
|| completion->status > WEBSCENE_OUTBOUND_DRAG_FAILED_V1
|| (completion->selected_operation & ~allowed_operations) != 0U
|| (completion->modifiers & ~allowed_modifiers) != 0U
|| !std::isfinite(completion->x) || !std::isfinite(completion->y)
|| (completion->status != WEBSCENE_OUTBOUND_DRAG_COMPLETED_V1
&& completion->selected_operation != 0U)) return 0U;
webscene_native::native_outbound_drag_completion owned;
owned.value = *completion;
return engine->complete_outbound_drag(std::move(owned)) ? 1U : 0U;
}
uint8_t webscene_engine_complete_file_request_v1(webscene_engine* engine,
uint64_t id, uint32_t status, const webscene_file_data_v1* files,
size_t count, const char* error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1870,6 +1870,73 @@ webscene_engine_take_download_request_v1(webscene_engine* engine);
WEBSCENE_API void webscene_download_request_release_v1(
const webscene_download_request_v1* request);

/* Immutable desktop-drag handoff. WebScene publishes only browser-admitted
* metadata; native objects, paths, destination policy, and grant resolution
* remain host-owned. A lease remains valid until release, including after
* engine teardown. Completion is copied and delivered on the script worker.
* Limits: 8 pending requests, 32 items, 1 MiB aggregate UTF-8 metadata,
* 8 KiB per URI, 256-byte MIME types, and 1 KiB opaque grant references. */
enum {
WEBSCENE_OUTBOUND_DRAG_OPERATION_COPY_V1 = 1U << 0U,
WEBSCENE_OUTBOUND_DRAG_OPERATION_LINK_V1 = 1U << 1U,
WEBSCENE_OUTBOUND_DRAG_OPERATION_MOVE_V1 = 1U << 2U,
WEBSCENE_OUTBOUND_DRAG_ITEM_TEXT_V1 = 1,
WEBSCENE_OUTBOUND_DRAG_ITEM_URI_V1 = 2,
WEBSCENE_OUTBOUND_DRAG_ITEM_IMAGE_V1 = 3,
WEBSCENE_OUTBOUND_DRAG_ITEM_FILE_GRANT_V1 = 4,
WEBSCENE_OUTBOUND_DRAG_COMPLETED_V1 = 1,
WEBSCENE_OUTBOUND_DRAG_CANCELLED_V1 = 2,
WEBSCENE_OUTBOUND_DRAG_FAILED_V1 = 3
};
typedef struct webscene_outbound_drag_item_v1 {
uint32_t struct_size, version;
uint32_t kind, reserved;
const char* mime_type;
size_t mime_type_length;
const char* value;
size_t value_length;
const char* name;
size_t name_length;
const char* grant_reference;
size_t grant_reference_length;
uint64_t byte_size;
} webscene_outbound_drag_item_v1;
typedef struct webscene_outbound_drag_request_v1 {
uint32_t struct_size, version;
uint64_t request_id;
uint64_t document_generation;
uint64_t node_generation;
uint64_t frame_generation;
uint64_t source_node_id;
uint64_t frame_owner_node_id;
uint32_t allowed_operations;
uint32_t item_count;
const webscene_outbound_drag_item_v1* items;
float pointer_x, pointer_y;
uint64_t drag_image_node_id;
float drag_image_hotspot_x, drag_image_hotspot_y;
float drag_image_width, drag_image_height;
const char* source_origin;
size_t source_origin_length;
} webscene_outbound_drag_request_v1;
typedef struct webscene_outbound_drag_completion_v1 {
uint32_t struct_size, version;
uint64_t request_id;
uint64_t document_generation;
uint32_t status;
uint32_t selected_operation;
double x, y;
uint32_t modifiers;
uint32_t reserved;
} webscene_outbound_drag_completion_v1;
WEBSCENE_API const webscene_outbound_drag_request_v1*
webscene_engine_take_outbound_drag_request_v1(webscene_engine* engine);
WEBSCENE_API void webscene_outbound_drag_request_release_v1(
const webscene_outbound_drag_request_v1* request);
WEBSCENE_API uint8_t webscene_engine_complete_outbound_drag_v1(
webscene_engine* engine,
const webscene_outbound_drag_completion_v1* completion);

/* Native file panel transport v2. This queue is independent of the v1
* byte-copy service. Requests contain presentation metadata and an optional
* opaque initial-location token. Successful results contain only bounded
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ using script_work_request =
webscene_native::file_grant_remove_completion_data_v2,
webscene_native::native_host_completion,
webscene_native::native_drag_event,
webscene_native::native_outbound_drag_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 @@ -187,6 +187,19 @@
signal_worker();
return true;
}
bool complete_outbound_drag(
webscene_native::native_outbound_drag_completion completion) {
#if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8)
std::lock_guard lock(script_mutex_);
if (script_work_.size() >= 1024U) return false;
script_work_.emplace_back(std::move(completion));
signal_worker();
return true;
#else
static_cast<void>(completion);
return false;
#endif
}
bool dispatch_drag(webscene_native::native_drag_event event) {
#if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8)
std::lock_guard lock(script_mutex_);
Expand Down Expand Up @@ -301,6 +314,14 @@
#if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8)
std::lock_guard lock(file_runtime_mutex_);
if (file_runtime_ready_) return runtime_->take_download_request();
#endif
return {};
}
std::unique_ptr<webscene_native::native_outbound_drag_request>
take_outbound_drag_request() {
#if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8)
std::lock_guard lock(file_runtime_mutex_);
if (file_runtime_ready_) return runtime_->take_outbound_drag_request();
#endif
return {};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,12 @@
changed = true;
continue;
}
if (auto* drag = std::get_if<
webscene_native::native_outbound_drag_completion>(&request)) {
if (runtime_) runtime_->complete_outbound_drag(*drag);
changed = true;
continue;
}
if (auto* panel = std::get_if<
webscene_native::file_panel_completion_data_v2>(&request)) {
if (runtime_) runtime_->complete_file_panel_request(*panel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5800,7 +5800,7 @@ struct v8_dom_runtime::implementation final {
return Object.freeze(list);
};
class WebSceneDataTransfer {
constructor(records = []) {
constructor(records = [], writable = false) {
const strings = Object.create(null);
const files = [];
const items = [];
Expand Down Expand Up @@ -5879,6 +5879,10 @@ struct v8_dom_runtime::implementation final {
this._items = indexedList(items);
this.effectAllowed = 'all';
this._dropEffect = 'none';
this._writable = writable;
this._dragImageElement = null;
this._dragImageX = 0;
this._dragImageY = 0;
}
get types() {
const types = Object.keys(this._strings);
Expand All @@ -5896,9 +5900,22 @@ struct v8_dom_runtime::implementation final {
this._dropEffect = value;
}
getData(type) { return this._strings[String(type).toLowerCase()] || ''; }
setData() {}
clearData() {}
setDragImage() {}
setData(type, value) {
if (!this._writable) return;
type = String(type).toLowerCase();
if (type) this._strings[type] = String(value);
}
clearData(type) {
if (!this._writable) return;
if (arguments.length === 0) this._strings = Object.create(null);
else delete this._strings[String(type).toLowerCase()];
}
setDragImage(element, x, y) {
if (!this._writable || !element || element.nodeType !== 1) return;
this._dragImageElement = element;
this._dragImageX = Number.isFinite(Number(x)) ? Number(x) : 0;
this._dragImageY = Number.isFinite(Number(y)) ? Number(y) : 0;
}
}
Object.defineProperties(globalThis, {
DataTransfer: { value: WebSceneDataTransfer, configurable: true },
Expand All @@ -5907,7 +5924,9 @@ struct v8_dom_runtime::implementation final {
},
__webSceneCreateDragDataTransfer: {
configurable: true,
value(records) { return new WebSceneDataTransfer(records); }
value(records, writable = false) {
return new WebSceneDataTransfer(records, writable);
}
}
});
})();
Expand Down Expand Up @@ -6492,6 +6511,7 @@ struct v8_dom_runtime::implementation final {
// Keep these fragments in one translation unit: their order and direct
// visibility preserve the runtime's existing release code generation.
#include "webscene_v8_runtime_interop.inc"
#include "webscene_v8_runtime_outbound_drag.inc"
#include "webscene_v8_runtime_tasks.inc"
#include "webscene_v8_runtime_resources.inc"
#include "webscene_v8_runtime_dom_core.inc"
Expand Down Expand Up @@ -8720,4 +8740,16 @@ std::unique_ptr<native_host_request> v8_dom_runtime::take_typed_host_request() {
std::unique_ptr<native_download_request> v8_dom_runtime::take_download_request() {
return impl_->take_download_request();
}
std::unique_ptr<native_outbound_drag_request>
v8_dom_runtime::take_outbound_drag_request() {
return impl_->take_outbound_drag_request();
}
void v8_dom_runtime::complete_outbound_drag(
native_outbound_drag_completion& completion) {
if (impl_ == nullptr) return;
v8::Locker locker(impl_->isolate);
v8::Isolate::Scope isolate_scope(impl_->isolate);
v8::HandleScope handles(impl_->isolate);
impl_->complete_outbound_drag(completion);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,43 @@ struct native_drag_event {
double y{};
std::vector<native_drag_item> items;
};
struct native_outbound_drag_item {
webscene_outbound_drag_item_v1 view{};
std::string mime_type;
std::string value;
std::string name;
std::string grant_reference;
void bind() {
view.struct_size = sizeof(view); view.version = 1;
view.mime_type = mime_type.empty() ? nullptr : mime_type.data();
view.mime_type_length = mime_type.size();
view.value = value.empty() ? nullptr : value.data();
view.value_length = value.size();
view.name = name.empty() ? nullptr : name.data();
view.name_length = name.size();
view.grant_reference = grant_reference.empty()
? nullptr : grant_reference.data();
view.grant_reference_length = grant_reference.size();
}
};
struct native_outbound_drag_request {
webscene_outbound_drag_request_v1 view{};
std::string source_origin;
std::vector<native_outbound_drag_item> owned_items;
std::vector<webscene_outbound_drag_item_v1> item_views;
void bind() {
item_views.clear(); item_views.reserve(owned_items.size());
for (auto& item : owned_items) { item.bind(); item_views.push_back(item.view); }
view.struct_size = sizeof(view); view.version = 1;
view.item_count = static_cast<uint32_t>(item_views.size());
view.items = item_views.empty() ? nullptr : item_views.data();
view.source_origin = source_origin.empty() ? nullptr : source_origin.data();
view.source_origin_length = source_origin.size();
}
};
struct native_outbound_drag_completion {
webscene_outbound_drag_completion_v1 value{};
};

class native_document;
struct dom_node;
Expand Down Expand Up @@ -413,6 +450,8 @@ class v8_dom_runtime final {
void set_native_media_policy(uint32_t flags);
std::unique_ptr<native_file_request> take_file_request();
std::unique_ptr<native_download_request> take_download_request();
std::unique_ptr<native_outbound_drag_request> take_outbound_drag_request();
void complete_outbound_drag(native_outbound_drag_completion& completion);
void complete_file_request(native_file_completion& completion);
void complete_file_panel_request(file_panel_completion_data_v2& completion);
void complete_file_grant_same_entry_request(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5855,6 +5855,18 @@
}

void clear_selections_containing(const dom_node &subtree) {
if (active_outbound_drag.has_value()) {
auto* source = document.find_by_native_id(
active_outbound_drag->source_node_id);
if (source != nullptr && subtree_contains(subtree, source)) {
active_outbound_drag.reset();
std::lock_guard lock(host_request_mutex);
std::erase_if(outbound_drag_requests, [&](const auto& request) {
return request != nullptr
&& request->view.source_node_id == source->id;
});
}
}
for (auto &[root_id, selection] : window_selections) {
static_cast<void>(root_id);
selection.text_generation = std::numeric_limits<uint64_t>::max();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
void retire_document_realm(v8::Local<v8::Context> previous_context)
{
active_drag_session.reset();
active_outbound_drag.reset();
outbound_drag_threshold_crossed = false;
std::vector<v8::Local<v8::Context>> retired_contexts{previous_context};
retired_contexts.reserve(actual_frame_contexts.size() + 1U);
for (auto& [id, frame] : actual_frame_contexts) {
Expand Down Expand Up @@ -143,6 +145,7 @@
{ std::lock_guard lock(host_request_mutex); host_requests.clear(); }
{ std::lock_guard lock(host_request_mutex); typed_host_requests.clear(); }
{ std::lock_guard lock(host_request_mutex); download_requests.clear(); }
{ std::lock_guard lock(host_request_mutex); outbound_drag_requests.clear(); }
object_url_file_data.clear();
release_owned_binary_object_urls();
object_urls.clear();
Expand Down
Loading
Loading