feat(servo): upgrade to latest secure upstream snapshot
This commit is contained in:
Generated
+1354
-1383
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -23,7 +23,7 @@ gpui = "0.2.2"
|
|||||||
gpui-component = "0.5.1"
|
gpui-component = "0.5.1"
|
||||||
gpui-component-assets = "0.5.1"
|
gpui-component-assets = "0.5.1"
|
||||||
image = "0.25.10"
|
image = "0.25.10"
|
||||||
servo = "0.1.0"
|
servo = { version = "0.4.0", git = "https://github.com/servo/servo.git", rev = "bc469fd5c17137373458508f88c3907cd1fcb69a" }
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
semver = "1.0.28"
|
semver = "1.0.28"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
|||||||
@@ -1788,12 +1788,12 @@ KV namespace: ELY_KV
|
|||||||
## 24. Reference
|
## 24. Reference
|
||||||
|
|
||||||
[R1] Google Chrome — Get more done with new vertical tabs and immersive reading mode in Chrome — https://blog.google/products-and-platforms/products/chrome/new-chrome-productivity-features/
|
[R1] Google Chrome — Get more done with new vertical tabs and immersive reading mode in Chrome — https://blog.google/products-and-platforms/products/chrome/new-chrome-productivity-features/
|
||||||
[R2] Microsoft Edge — Vertical Tabs — https://www.microsoft.com/en-us/edge/features/vertical-tabs
|
[R2] Microsoft Edge — Vertical Tabs — https://explore.microsoft.com/en-us/edge/features/vertical-tabs?form=MT0160 <br>
|
||||||
[R3] Arc Help Center — Favorites: Top Tabs Across Every Space — https://resources.arc.net/hc/en-us/articles/19230755904151-Favorites-Top-Tabs-Across-Every-Space
|
[R3] Arc Help Center — Favorites: Top Tabs Across Every Space — https://resources.arc.net/hc/en-us/articles/19230755904151-Favorites-Top-Tabs-Across-Every-Space
|
||||||
[R4] Arc Help Center — Auto Archive: Clean as you go — https://resources.arc.net/hc/en-us/articles/19228855311127-Auto-Archive-Clean-as-you-go
|
[R4] Arc Help Center — Auto Archive: Clean as you go — https://resources.arc.net/hc/en-us/articles/19228855311127-Auto-Archive-Clean-as-you-go
|
||||||
[R5] Vivaldi — Workspaces — https://vivaldi.com/features/workspaces/
|
[R5] Vivaldi — Workspaces — https://vivaldi.com/features/workspaces/
|
||||||
[R6] Vivaldi Help — Tab Tiling — https://help.vivaldi.com/desktop/tabs/tab-tiling/
|
[R6] Vivaldi Help — Tab Tiling — https://help.vivaldi.com/desktop/tabs/tab-tiling/
|
||||||
[R7] GPUI official — https://www.gpui.rs/
|
[R7] GPUI official — https://gpui.rs/ <br>
|
||||||
[R8] docs.rs — GPUI — https://docs.rs/gpui
|
[R8] docs.rs — GPUI — https://docs.rs/gpui
|
||||||
[R9] Zed Blog — Leveraging Rust and the GPU to render user interfaces at 120 FPS — https://zed.dev/blog/videogame
|
[R9] Zed Blog — Leveraging Rust and the GPU to render user interfaces at 120 FPS — https://zed.dev/blog/videogame
|
||||||
[R10] gpui-component — https://github.com/longbridge/gpui-component
|
[R10] gpui-component — https://github.com/longbridge/gpui-component
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub(crate) const PRD_REFERENCE_SITE_CASES: &[LiveSiteCase] = &[
|
|||||||
title_fragment: "Chrome",
|
title_fragment: "Chrome",
|
||||||
},
|
},
|
||||||
LiveSiteCase {
|
LiveSiteCase {
|
||||||
url: "https://www.microsoft.com/en-us/edge/features/vertical-tabs",
|
url: "https://explore.microsoft.com/en-us/edge/features/vertical-tabs?form=MT0160",
|
||||||
title_fragment: "Microsoft Edge",
|
title_fragment: "Microsoft Edge",
|
||||||
},
|
},
|
||||||
LiveSiteCase {
|
LiveSiteCase {
|
||||||
@@ -28,7 +28,7 @@ pub(crate) const PRD_REFERENCE_SITE_CASES: &[LiveSiteCase] = &[
|
|||||||
url: "https://help.vivaldi.com/desktop/tabs/tab-tiling/",
|
url: "https://help.vivaldi.com/desktop/tabs/tab-tiling/",
|
||||||
title_fragment: "Tab Tiling",
|
title_fragment: "Tab Tiling",
|
||||||
},
|
},
|
||||||
LiveSiteCase { url: "https://www.gpui.rs/", title_fragment: "gpui" },
|
LiveSiteCase { url: "https://gpui.rs/", title_fragment: "gpui" },
|
||||||
LiveSiteCase { url: "https://docs.rs/gpui", title_fragment: "gpui" },
|
LiveSiteCase { url: "https://docs.rs/gpui", title_fragment: "gpui" },
|
||||||
LiveSiteCase { url: "https://zed.dev/blog/videogame", title_fragment: "Leveraging Rust" },
|
LiveSiteCase { url: "https://zed.dev/blog/videogame", title_fragment: "Leveraging Rust" },
|
||||||
LiveSiteCase {
|
LiveSiteCase {
|
||||||
|
|||||||
@@ -47,9 +47,6 @@ pub struct SoftwareServoHost {
|
|||||||
default_surface_size: ServoSurfaceSize,
|
default_surface_size: ServoSurfaceSize,
|
||||||
rendering_context_kind: RenderingContextKind,
|
rendering_context_kind: RenderingContextKind,
|
||||||
webviews: HashMap<WebViewId, HostWebView>,
|
webviews: HashMap<WebViewId, HostWebView>,
|
||||||
// Servo 0.1.0 can still run script tasks after a remote page moves away.
|
|
||||||
// Retain hidden about:blank WebViews until host shutdown releases them together.
|
|
||||||
retired_webviews: Vec<HostWebView>,
|
|
||||||
permissions: PermissionStore,
|
permissions: PermissionStore,
|
||||||
wake_requested: Arc<AtomicBool>,
|
wake_requested: Arc<AtomicBool>,
|
||||||
last_rendered_frame: Option<RenderedFrame>,
|
last_rendered_frame: Option<RenderedFrame>,
|
||||||
@@ -112,27 +109,7 @@ impl SoftwareServoHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_webview(&mut self, webview_id: &WebViewId) -> bool {
|
pub fn close_webview(&mut self, webview_id: &WebViewId) -> bool {
|
||||||
let Some(webview) = self.webviews.remove(webview_id) else {
|
self.webviews.remove(webview_id).is_some()
|
||||||
return false;
|
|
||||||
};
|
|
||||||
self.retire_webview(webview);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn retire_webview(&mut self, webview: HostWebView) {
|
|
||||||
webview.webview.hide();
|
|
||||||
if let Ok(blank_url) = Url::parse("about:blank") {
|
|
||||||
webview.webview.load(blank_url);
|
|
||||||
}
|
|
||||||
for _ in 0..32 {
|
|
||||||
self.servo.spin_event_loop();
|
|
||||||
if webview.current_url().as_deref() == Some("about:blank")
|
|
||||||
&& matches!(webview.state(), WebViewState::Complete)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.retired_webviews.push(webview);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_started(
|
fn new_started(
|
||||||
@@ -155,7 +132,6 @@ impl SoftwareServoHost {
|
|||||||
default_surface_size: size,
|
default_surface_size: size,
|
||||||
rendering_context_kind,
|
rendering_context_kind,
|
||||||
webviews: HashMap::new(),
|
webviews: HashMap::new(),
|
||||||
retired_webviews: Vec::new(),
|
|
||||||
permissions: Rc::new(RefCell::new(HashMap::new())),
|
permissions: Rc::new(RefCell::new(HashMap::new())),
|
||||||
wake_requested,
|
wake_requested,
|
||||||
last_rendered_frame: None,
|
last_rendered_frame: None,
|
||||||
@@ -399,11 +375,7 @@ impl ServoHost for SoftwareServoHost {
|
|||||||
|
|
||||||
impl Drop for SoftwareServoHost {
|
impl Drop for SoftwareServoHost {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let webviews = std::mem::take(&mut self.webviews);
|
self.webviews.clear();
|
||||||
for webview in webviews.into_values() {
|
|
||||||
self.retire_webview(webview);
|
|
||||||
}
|
|
||||||
self.retired_webviews.clear();
|
|
||||||
self.drain_after_webview_close();
|
self.drain_after_webview_close();
|
||||||
SERVO_RUNTIME_STARTED.store(false, Ordering::Release);
|
SERVO_RUNTIME_STARTED.store(false, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use servo::{
|
use servo::{
|
||||||
DevicePoint, InputEvent, KeyState, KeyboardEvent, Location, Modifiers, MouseButton,
|
DevicePoint, InputEvent, KeyState, KeyboardEvent, Location, Modifiers, MouseButton,
|
||||||
MouseButtonAction, MouseButtonEvent, MouseMoveEvent, TouchEvent, TouchEventType, TouchId,
|
MouseButtonAction, MouseButtonEvent, MouseMoveEvent, TouchEvent, TouchEventType, TouchId,
|
||||||
WebView, WebViewPoint,
|
TouchPointerType, WebView, WebViewPoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::keyboard::key_and_code_for_character;
|
use crate::keyboard::key_and_code_for_character;
|
||||||
@@ -30,7 +30,12 @@ pub(super) fn send_touch_tap(webview: &WebView, x: u32, y: u32) {
|
|||||||
let point = point(x, y);
|
let point = point(x, y);
|
||||||
let touch_id = TouchId(1);
|
let touch_id = TouchId(1);
|
||||||
for event_type in [TouchEventType::Down, TouchEventType::Up] {
|
for event_type in [TouchEventType::Down, TouchEventType::Up] {
|
||||||
webview.notify_input_event(InputEvent::Touch(TouchEvent::new(event_type, touch_id, point)));
|
webview.notify_input_event(InputEvent::Touch(TouchEvent::new(
|
||||||
|
event_type,
|
||||||
|
touch_id,
|
||||||
|
point,
|
||||||
|
TouchPointerType::Touch,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,11 +75,13 @@ fn site_permission_feature_for_servo(
|
|||||||
servo::PermissionFeature::PersistentStorage => {
|
servo::PermissionFeature::PersistentStorage => {
|
||||||
Some(SitePermissionFeature::StoragePersistence)
|
Some(SitePermissionFeature::StoragePersistence)
|
||||||
}
|
}
|
||||||
servo::PermissionFeature::Push
|
servo::PermissionFeature::ScreenWakeLock(_)
|
||||||
|
| servo::PermissionFeature::Push
|
||||||
| servo::PermissionFeature::Midi
|
| servo::PermissionFeature::Midi
|
||||||
| servo::PermissionFeature::Speaker
|
| servo::PermissionFeature::Speaker
|
||||||
| servo::PermissionFeature::DeviceInfo
|
| servo::PermissionFeature::DeviceInfo
|
||||||
| servo::PermissionFeature::BackgroundSync
|
| servo::PermissionFeature::BackgroundSync
|
||||||
|
| servo::PermissionFeature::Gamepad
|
||||||
| servo::PermissionFeature::Bluetooth => None,
|
| servo::PermissionFeature::Bluetooth => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +92,20 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{PermissionDecision, PermissionRequest};
|
use crate::{PermissionDecision, PermissionRequest};
|
||||||
|
|
||||||
use super::{PermissionStore, set_permission_decision, take_permission_decision};
|
use super::{
|
||||||
|
PermissionStore, set_permission_decision, site_permission_feature_for_servo,
|
||||||
|
take_permission_decision,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keeps_disabled_servo_permissions_out_of_site_settings() {
|
||||||
|
for feature in [
|
||||||
|
servo::PermissionFeature::ScreenWakeLock(servo::WakeLockType::Screen),
|
||||||
|
servo::PermissionFeature::Gamepad,
|
||||||
|
] {
|
||||||
|
assert_eq!(site_permission_feature_for_servo(feature), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn allow_once_is_consumed_after_one_matching_origin_request()
|
fn allow_once_is_consumed_after_one_matching_origin_request()
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const SOFTWARE_HOST_CHILD_ENV: &str = "ELY_SERVO_SOFTWARE_HOST_CHILD";
|
|||||||
const DPR_VIEWPORT_CHILD_ENV: &str = "ELY_SERVO_DPR_VIEWPORT_CHILD";
|
const DPR_VIEWPORT_CHILD_ENV: &str = "ELY_SERVO_DPR_VIEWPORT_CHILD";
|
||||||
const CLICK_PROBE_URL: &str = "data:text/html,%3C!doctype%20html%3E%3Ctitle%3EClick%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3B%7D%3C%2Fstyle%3E%3Cbutton%20onclick%3D%22document.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Clicked%27%3Bthis.textContent%3D%27Clicked%27%3B%22%3ETap%3C%2Fbutton%3E";
|
const CLICK_PROBE_URL: &str = "data:text/html,%3C!doctype%20html%3E%3Ctitle%3EClick%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3B%7D%3C%2Fstyle%3E%3Cbutton%20onclick%3D%22document.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Clicked%27%3Bthis.textContent%3D%27Clicked%27%3B%22%3ETap%3C%2Fbutton%3E";
|
||||||
const DRAG_PROBE_URL: &str = "data:text/html,%3C%21doctype%20html%3E%3Ctitle%3EDrag%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3B%7D%3C%2Fstyle%3E%3Cbutton%20id%3Dbox%3EDrag%3C%2Fbutton%3E%3Cscript%3Elet%20dragging%3Dfalse%3Bconst%20box%3Ddocument.getElementById%28%27box%27%29%3BaddEventListener%28%27mousedown%27%2Cevent%3D%3E%7Bif%28event.target%3D%3D%3Dbox%29%7Bdragging%3Dtrue%3B%7D%7D%29%3BaddEventListener%28%27mousemove%27%2Cevent%3D%3E%7Bif%28dragging%26%26event.clientX%3E280%29%7Bdocument.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Dragged%27%3Bbox.textContent%3D%27Dragged%27%3B%7D%7D%29%3BaddEventListener%28%27mouseup%27%2C%28%29%3D%3E%7Bdragging%3Dfalse%3B%7D%29%3B%3C%2Fscript%3E";
|
const DRAG_PROBE_URL: &str = "data:text/html,%3C%21doctype%20html%3E%3Ctitle%3EDrag%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3B%7D%3C%2Fstyle%3E%3Cbutton%20id%3Dbox%3EDrag%3C%2Fbutton%3E%3Cscript%3Elet%20dragging%3Dfalse%3Bconst%20box%3Ddocument.getElementById%28%27box%27%29%3BaddEventListener%28%27mousedown%27%2Cevent%3D%3E%7Bif%28event.target%3D%3D%3Dbox%29%7Bdragging%3Dtrue%3B%7D%7D%29%3BaddEventListener%28%27mousemove%27%2Cevent%3D%3E%7Bif%28dragging%26%26event.clientX%3E280%29%7Bdocument.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Dragged%27%3Bbox.textContent%3D%27Dragged%27%3B%7D%7D%29%3BaddEventListener%28%27mouseup%27%2C%28%29%3D%3E%7Bdragging%3Dfalse%3B%7D%29%3B%3C%2Fscript%3E";
|
||||||
const TOUCH_PROBE_URL: &str = "data:text/html,%3C%21doctype%20html%3E%3Ctitle%3ETouch%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3Btouch-action%3Amanipulation%3B%7D%3C%2Fstyle%3E%3Cbutton%20ontouchstart%3D%22document.body.dataset.touch%3D%27start%27%3B%22%20onclick%3D%22document.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Touched%27%3Bthis.textContent%3D%27Touched%27%3B%22%3ETap%3C%2Fbutton%3E";
|
const TOUCH_PROBE_URL: &str = "data:text/html,%3C%21doctype%20html%3E%3Ctitle%3ETouch%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3Btouch-action%3Amanipulation%3B%7D%3C%2Fstyle%3E%3Cbutton%20ontouchstart%3D%22document.body.dataset.touch%3D%27start%27%3B%22%20onpointerdown%3D%22if%28%21document.body.dataset.pointerType%29%7Bdocument.body.dataset.pointerType%3Devent.pointerType%3B%7D%22%20onclick%3D%22if%28document.body.dataset.pointerType%21%3D%3D%27touch%27%29%7Bdocument.title%3Ddocument.body.dataset.pointerType%3Breturn%3B%7Ddocument.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Touched%27%3Bthis.textContent%3D%27Touched%27%3B%22%3ETap%3C%2Fbutton%3E";
|
||||||
const TEXT_PROBE_URL: &str = "data:text/html,%3C!doctype%20html%3E%3Ctitle%3EText%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3Bfont%3A28px%20sans-serif%3B%7Dinput%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A260px%3Bheight%3A70px%3Bfont%3A28px%20sans-serif%3B%7Doutput%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A180px%3Bfont%3A32px%20sans-serif%3B%7D%3C%2Fstyle%3E%3Cinput%20id%3Dq%20autofocus%20oninput%3D%22document.body.style.background%3D%27%230039ff%27%3Bdocument.getElementById%28%27out%27%29.textContent%3Dthis.value%3B%22%3E%3Coutput%20id%3Dout%3Eempty%3C%2Foutput%3E";
|
const TEXT_PROBE_URL: &str = "data:text/html,%3C!doctype%20html%3E%3Ctitle%3EText%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3Bfont%3A28px%20sans-serif%3B%7Dinput%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A260px%3Bheight%3A70px%3Bfont%3A28px%20sans-serif%3B%7Doutput%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A180px%3Bfont%3A32px%20sans-serif%3B%7D%3C%2Fstyle%3E%3Cinput%20id%3Dq%20autofocus%20oninput%3D%22document.body.style.background%3D%27%230039ff%27%3Bdocument.getElementById%28%27out%27%29.textContent%3Dthis.value%3B%22%3E%3Coutput%20id%3Dout%3Eempty%3C%2Foutput%3E";
|
||||||
const TEXT_PROBE_VALUE: &str = "ely42";
|
const TEXT_PROBE_VALUE: &str = "ely42";
|
||||||
|
|
||||||
@@ -294,6 +294,7 @@ fn exercise_real_servo_webview_lifecycle() -> Result<(), Box<dyn Error>> {
|
|||||||
host.touch_tap(TouchTapRequest { webview_id: webview_id.clone(), x: 160, y: 120 })?;
|
host.touch_tap(TouchTapRequest { webview_id: webview_id.clone(), x: 160, y: 120 })?;
|
||||||
let snapshot = wait_for_rendered_webview(&mut host, &webview_id, Some(previous_frame_hash))?;
|
let snapshot = wait_for_rendered_webview(&mut host, &webview_id, Some(previous_frame_hash))?;
|
||||||
assert_eq!(snapshot.state(), &WebViewState::Complete, "snapshot: {snapshot:?}");
|
assert_eq!(snapshot.state(), &WebViewState::Complete, "snapshot: {snapshot:?}");
|
||||||
|
assert_eq!(snapshot.title(), Some("Touched"), "snapshot: {snapshot:?}");
|
||||||
assert_rendered_frame_has_content(&host, "data:text/html touched", 1)?;
|
assert_rendered_frame_has_content(&host, "data:text/html touched", 1)?;
|
||||||
assert_ne!(host.last_rendered_frame()?.sample_hash(), previous_frame_hash);
|
assert_ne!(host.last_rendered_frame()?.sample_hash(), previous_frame_hash);
|
||||||
|
|
||||||
@@ -368,6 +369,20 @@ fn exercise_real_servo_webview_lifecycle() -> Result<(), Box<dyn Error>> {
|
|||||||
)?;
|
)?;
|
||||||
assert_ne!(host.last_rendered_frame()?.sample_hash(), previous_frame_hash);
|
assert_ne!(host.last_rendered_frame()?.sample_hash(), previous_frame_hash);
|
||||||
|
|
||||||
|
assert!(host.close_webview(&webview_id));
|
||||||
|
assert!(!host.close_webview(&webview_id));
|
||||||
|
assert!(matches!(host.snapshot(&webview_id), Err(ServoHostError::WebViewNotFound { .. })));
|
||||||
|
|
||||||
|
let replacement_tab_id = TabId::new();
|
||||||
|
let replacement_id = host.create_webview(replacement_tab_id.clone(), profile_id)?;
|
||||||
|
host.navigate(NavigationRequest {
|
||||||
|
webview_id: replacement_id.clone(),
|
||||||
|
tab_id: replacement_tab_id,
|
||||||
|
url: UrlText::parse(CLICK_PROBE_URL)?,
|
||||||
|
})?;
|
||||||
|
let replacement = wait_for_rendered_webview(&mut host, &replacement_id, None)?;
|
||||||
|
assert_eq!(replacement.state(), &WebViewState::Complete, "replacement: {replacement:?}");
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
SoftwareServoHost::new(ServoSurfaceSize::new(INITIAL_WIDTH, INITIAL_HEIGHT)),
|
SoftwareServoHost::new(ServoSurfaceSize::new(INITIAL_WIDTH, INITIAL_HEIGHT)),
|
||||||
Err(ServoHostError::RuntimeAlreadyStarted)
|
Err(ServoHostError::RuntimeAlreadyStarted)
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ Current platform child surfaces:
|
|||||||
|
|
||||||
## Upstream Servo Route
|
## Upstream Servo Route
|
||||||
|
|
||||||
|
ELY pins Servo upstream commit `bc469fd5c17137373458508f88c3907cd1fcb69a` (workspace version
|
||||||
|
`0.4.0`) in `Cargo.toml` and `Cargo.lock`. That revision includes the July 2026 security fixes that
|
||||||
|
landed after the `v0.3.0` regular release. Servo's LTS line uses a separate six-month cadence.
|
||||||
|
|
||||||
Servo's own shell route is the model for the final ELY rendering path:
|
Servo's own shell route is the model for the final ELY rendering path:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -58,7 +62,7 @@ Window event
|
|||||||
-> RenderingContext::present
|
-> RenderingContext::present
|
||||||
```
|
```
|
||||||
|
|
||||||
Relevant upstream evidence from Servo `7c48af7`:
|
Relevant upstream evidence from Servo `bc469fd5c17137373458508f88c3907cd1fcb69a`:
|
||||||
|
|
||||||
- `ports/servoshell/window.rs` creates `WebViewBuilder::new(state.servo(), platform_window.rendering_context())`.
|
- `ports/servoshell/window.rs` creates `WebViewBuilder::new(state.servo(), platform_window.rendering_context())`.
|
||||||
- `ports/servoshell/window.rs` repaints with `webview.paint()` and `rendering_context().present()`.
|
- `ports/servoshell/window.rs` repaints with `webview.paint()` and `rendering_context().present()`.
|
||||||
|
|||||||
+58
-12
@@ -2,8 +2,9 @@
|
|||||||
# T16 — real-window screencapture sanity check.
|
# T16 — real-window screencapture sanity check.
|
||||||
#
|
#
|
||||||
# Boots ./target/release/ely_app in the background, opens a live page through
|
# Boots ./target/release/ely_app in the background, opens a live page through
|
||||||
# Servo's native surface path, grabs a screencapture, then asserts the PNG is
|
# Servo's native surface path, captures the ELY window by its Core Graphics
|
||||||
# non-trivial (size + dimensions + non-white center). Stderr from the app is
|
# window ID, then asserts the PNG is non-trivial (size + dimensions + non-white
|
||||||
|
# center). Stderr from the app is
|
||||||
# tee'd to a log so a crash leaves evidence behind.
|
# tee'd to a log so a crash leaves evidence behind.
|
||||||
#
|
#
|
||||||
# Idempotent: kills any leftover ely_app processes from prior runs before
|
# Idempotent: kills any leftover ely_app processes from prior runs before
|
||||||
@@ -52,8 +53,8 @@ pkill -f "target/release/ely_app" 2>/dev/null || true
|
|||||||
pkill -x "ely_app" 2>/dev/null || true
|
pkill -x "ely_app" 2>/dev/null || true
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
|
|
||||||
echo "[2/6] cargo build --release -p ely_app"
|
echo "[2/6] cargo build --release --locked -p ely_app"
|
||||||
if ! cargo build --release -p ely_app; then
|
if ! cargo build --release --locked -p ely_app; then
|
||||||
fail "cargo build failed"
|
fail "cargo build failed"
|
||||||
fi
|
fi
|
||||||
[[ -x "${APP_BIN}" ]] || fail "binary missing: ${APP_BIN}"
|
[[ -x "${APP_BIN}" ]] || fail "binary missing: ${APP_BIN}"
|
||||||
@@ -82,10 +83,52 @@ then
|
|||||||
fail "could not activate ely_app process ${APP_PID}"
|
fail "could not activate ely_app process ${APP_PID}"
|
||||||
fi
|
fi
|
||||||
sleep 1
|
sleep 1
|
||||||
# -x silences the shutter sound. Full screen is safer than -l <windowid> here
|
FRONTMOST=$(osascript -e \
|
||||||
# because we don't have a stable Cocoa window id; the app paints into the
|
"tell application \"System Events\" to get frontmost of first process whose unix id is ${APP_PID}")
|
||||||
# primary display and that's what we care about.
|
[[ "${FRONTMOST}" == "true" ]] || fail "ely_app process ${APP_PID} is not frontmost"
|
||||||
if ! screencapture -x "${SHOT_PATH}"; then
|
|
||||||
|
WINDOW_ID=$(/usr/bin/swift -e '
|
||||||
|
import CoreGraphics
|
||||||
|
import Darwin
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
guard CommandLine.arguments.count == 2, let targetPID = Int32(CommandLine.arguments[1]) else {
|
||||||
|
exit(64)
|
||||||
|
}
|
||||||
|
let windows = CGWindowListCopyWindowInfo(
|
||||||
|
[.optionOnScreenOnly, .excludeDesktopElements],
|
||||||
|
kCGNullWindowID
|
||||||
|
) as? [[String: Any]] ?? []
|
||||||
|
|
||||||
|
let candidates = windows.compactMap { window -> (number: UInt32, area: Double)? in
|
||||||
|
guard
|
||||||
|
let owner = window[kCGWindowOwnerPID as String] as? NSNumber,
|
||||||
|
owner.int32Value == targetPID,
|
||||||
|
let layer = window[kCGWindowLayer as String] as? NSNumber,
|
||||||
|
layer.intValue == 0,
|
||||||
|
let number = window[kCGWindowNumber as String] as? NSNumber,
|
||||||
|
let bounds = window[kCGWindowBounds as String] as? [String: Any],
|
||||||
|
let width = bounds["Width"] as? NSNumber,
|
||||||
|
let height = bounds["Height"] as? NSNumber,
|
||||||
|
width.doubleValue > 100,
|
||||||
|
height.doubleValue > 100
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return (number.uint32Value, width.doubleValue * height.doubleValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let window = candidates.max(by: { $0.area < $1.area }) else {
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
print(window.number)
|
||||||
|
' "${APP_PID}")
|
||||||
|
[[ "${WINDOW_ID}" =~ ^[0-9]+$ ]] || fail "could not resolve ELY window for pid ${APP_PID}"
|
||||||
|
echo " window_id=${WINDOW_ID}"
|
||||||
|
|
||||||
|
# -x silences the shutter sound; -l limits capture to the verified ELY window.
|
||||||
|
if ! screencapture -x -l "${WINDOW_ID}" "${SHOT_PATH}"; then
|
||||||
fail "screencapture invocation failed"
|
fail "screencapture invocation failed"
|
||||||
fi
|
fi
|
||||||
[[ -f "${SHOT_PATH}" ]] || fail "screencapture produced no file"
|
[[ -f "${SHOT_PATH}" ]] || fail "screencapture produced no file"
|
||||||
@@ -131,7 +174,7 @@ path = sys.argv[1]
|
|||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
if data[:8] != b"\x89PNG\r\n\x1a\n":
|
if data[:8] != b"\x89PNG\r\n\x1a\n":
|
||||||
print("SKIP not-png"); sys.exit(0)
|
print("ERROR not-png"); sys.exit(3)
|
||||||
|
|
||||||
i = 8
|
i = 8
|
||||||
width = height = bit_depth = color_type = None
|
width = height = bit_depth = color_type = None
|
||||||
@@ -149,8 +192,8 @@ while i < len(data):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if bit_depth != 8 or color_type not in (2, 6):
|
if bit_depth != 8 or color_type not in (2, 6):
|
||||||
print(f"SKIP unsupported bit_depth={bit_depth} color_type={color_type}")
|
print(f"ERROR unsupported bit_depth={bit_depth} color_type={color_type}")
|
||||||
sys.exit(0)
|
sys.exit(3)
|
||||||
|
|
||||||
channels = 3 if color_type == 2 else 4
|
channels = 3 if color_type == 2 else 4
|
||||||
stride = width * channels
|
stride = width * channels
|
||||||
@@ -184,7 +227,7 @@ for y in range(height):
|
|||||||
pr = a if pa <= pb and pa <= pc else (b if pb <= pc else c)
|
pr = a if pa <= pb and pa <= pc else (b if pb <= pc else c)
|
||||||
row[x] = (row[x] + pr) & 0xFF
|
row[x] = (row[x] + pr) & 0xFF
|
||||||
else:
|
else:
|
||||||
print(f"SKIP filter={f}"); sys.exit(0)
|
print(f"ERROR filter={f}"); sys.exit(3)
|
||||||
out[y*stride:(y+1)*stride] = row
|
out[y*stride:(y+1)*stride] = row
|
||||||
prev = row
|
prev = row
|
||||||
|
|
||||||
@@ -207,6 +250,9 @@ rm -f "${CENTER_PATCH}"
|
|||||||
if (( PY_RC == 2 )); then
|
if (( PY_RC == 2 )); then
|
||||||
fail "center ${PATCH_PX}x${PATCH_PX} patch is ~white — likely empty desktop or failed paint"
|
fail "center ${PATCH_PX}x${PATCH_PX} patch is ~white — likely empty desktop or failed paint"
|
||||||
fi
|
fi
|
||||||
|
if (( PY_RC != 0 )); then
|
||||||
|
fail "screenshot decoder rejected the center patch (exit ${PY_RC})"
|
||||||
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "PASS"
|
echo "PASS"
|
||||||
|
|||||||
Reference in New Issue
Block a user