mirror of
https://github.com/servo/servo.git
synced 2025-08-02 12:10:29 +01:00
constellation: restructure navigation, remove sync comm
This commit is contained in:
parent
f3ca48206e
commit
483bf245df
8 changed files with 100 additions and 39 deletions
|
@ -9,7 +9,7 @@ use euclid::TypedScale;
|
||||||
#[cfg(feature = "gl")]
|
#[cfg(feature = "gl")]
|
||||||
use gleam::gl;
|
use gleam::gl;
|
||||||
use keyboard_types::KeyboardEvent;
|
use keyboard_types::KeyboardEvent;
|
||||||
use msg::constellation_msg::{TopLevelBrowsingContextId, TraversalDirection};
|
use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId, TraversalDirection};
|
||||||
use script_traits::{MouseButton, TouchEventType, TouchId};
|
use script_traits::{MouseButton, TouchEventType, TouchId};
|
||||||
use servo_geometry::DeviceIndependentPixel;
|
use servo_geometry::DeviceIndependentPixel;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
@ -49,6 +49,8 @@ pub enum WindowEvent {
|
||||||
Refresh,
|
Refresh,
|
||||||
/// Sent when the window is resized.
|
/// Sent when the window is resized.
|
||||||
Resize,
|
Resize,
|
||||||
|
/// Sent when a navigation request from script is allowed/refused.
|
||||||
|
AllowNavigationResponse(PipelineId, bool),
|
||||||
/// Sent when a new URL is to be loaded.
|
/// Sent when a new URL is to be loaded.
|
||||||
LoadUrl(TopLevelBrowsingContextId, ServoUrl),
|
LoadUrl(TopLevelBrowsingContextId, ServoUrl),
|
||||||
/// Sent when a mouse hit test is to be performed.
|
/// Sent when a mouse hit test is to be performed.
|
||||||
|
@ -96,6 +98,7 @@ impl Debug for WindowEvent {
|
||||||
WindowEvent::Refresh => write!(f, "Refresh"),
|
WindowEvent::Refresh => write!(f, "Refresh"),
|
||||||
WindowEvent::Resize => write!(f, "Resize"),
|
WindowEvent::Resize => write!(f, "Resize"),
|
||||||
WindowEvent::Keyboard(..) => write!(f, "Keyboard"),
|
WindowEvent::Keyboard(..) => write!(f, "Keyboard"),
|
||||||
|
WindowEvent::AllowNavigationResponse(..) => write!(f, "AllowNavigationResponse"),
|
||||||
WindowEvent::LoadUrl(..) => write!(f, "LoadUrl"),
|
WindowEvent::LoadUrl(..) => write!(f, "LoadUrl"),
|
||||||
WindowEvent::MouseWindowEventClass(..) => write!(f, "Mouse"),
|
WindowEvent::MouseWindowEventClass(..) => write!(f, "Mouse"),
|
||||||
WindowEvent::MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
|
WindowEvent::MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
|
||||||
|
|
|
@ -156,6 +156,7 @@ use servo_rand::{random, Rng, SeedableRng, ServoRng};
|
||||||
use servo_remutex::ReentrantMutex;
|
use servo_remutex::ReentrantMutex;
|
||||||
use servo_url::{Host, ImmutableOrigin, ServoUrl};
|
use servo_url::{Host, ImmutableOrigin, ServoUrl};
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::replace;
|
use std::mem::replace;
|
||||||
|
@ -168,6 +169,8 @@ use style_traits::viewport::ViewportConstraints;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
use webvr_traits::{WebVREvent, WebVRMsg};
|
use webvr_traits::{WebVREvent, WebVRMsg};
|
||||||
|
|
||||||
|
type PendingApprovalNavigations = HashMap<PipelineId, (LoadData, bool)>;
|
||||||
|
|
||||||
/// Servo supports tabs (referred to as browsers), so `Constellation` needs to
|
/// Servo supports tabs (referred to as browsers), so `Constellation` needs to
|
||||||
/// store browser specific data for bookkeeping.
|
/// store browser specific data for bookkeeping.
|
||||||
struct Browser {
|
struct Browser {
|
||||||
|
@ -366,6 +369,9 @@ pub struct Constellation<Message, LTF, STF> {
|
||||||
|
|
||||||
/// A channel through which messages can be sent to the canvas paint thread.
|
/// A channel through which messages can be sent to the canvas paint thread.
|
||||||
canvas_chan: IpcSender<CanvasMsg>,
|
canvas_chan: IpcSender<CanvasMsg>,
|
||||||
|
|
||||||
|
/// Navigation requests from script awaiting approval from the embedder.
|
||||||
|
pending_approval_navigations: PendingApprovalNavigations,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State needed to construct a constellation.
|
/// State needed to construct a constellation.
|
||||||
|
@ -698,6 +704,7 @@ where
|
||||||
webgl_threads: state.webgl_threads,
|
webgl_threads: state.webgl_threads,
|
||||||
webvr_chan: state.webvr_chan,
|
webvr_chan: state.webvr_chan,
|
||||||
canvas_chan: CanvasPaintThread::start(),
|
canvas_chan: CanvasPaintThread::start(),
|
||||||
|
pending_approval_navigations: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
constellation.run();
|
constellation.run();
|
||||||
|
@ -1044,6 +1051,36 @@ where
|
||||||
FromCompositorMsg::Keyboard(key_event) => {
|
FromCompositorMsg::Keyboard(key_event) => {
|
||||||
self.handle_key_msg(key_event);
|
self.handle_key_msg(key_event);
|
||||||
},
|
},
|
||||||
|
// Perform a navigation previously requested by script, if approved by the embedder.
|
||||||
|
// If there is already a pending page (self.pending_changes), it will not be overridden;
|
||||||
|
// However, if the id is not encompassed by another change, it will be.
|
||||||
|
FromCompositorMsg::AllowNavigationResponse(pipeline_id, allowed) => {
|
||||||
|
let pending = self.pending_approval_navigations.remove(&pipeline_id);
|
||||||
|
|
||||||
|
let top_level_browsing_context_id = match self.pipelines.get(&pipeline_id) {
|
||||||
|
Some(pipeline) => pipeline.top_level_browsing_context_id,
|
||||||
|
None => return warn!("Attempted to navigate {} after closure.", pipeline_id),
|
||||||
|
};
|
||||||
|
|
||||||
|
match pending {
|
||||||
|
Some((load_data, replace)) => {
|
||||||
|
if allowed {
|
||||||
|
self.load_url(
|
||||||
|
top_level_browsing_context_id,
|
||||||
|
pipeline_id,
|
||||||
|
load_data,
|
||||||
|
replace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
return warn!(
|
||||||
|
"AllowNavigationReqsponse for unknow request: {:?}",
|
||||||
|
pipeline_id
|
||||||
|
)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
// Load a new page from a typed url
|
// Load a new page from a typed url
|
||||||
// If there is already a pending page (self.pending_changes), it will not be overridden;
|
// If there is already a pending page (self.pending_changes), it will not be overridden;
|
||||||
// However, if the id is not encompassed by another change, it will be.
|
// However, if the id is not encompassed by another change, it will be.
|
||||||
|
@ -1059,12 +1096,9 @@ where
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
self.handle_load_url_msg(
|
// Since this is a top-level load, initiated by the embedder, go straight to load_url,
|
||||||
top_level_browsing_context_id,
|
// bypassing schedule_navigation.
|
||||||
pipeline_id,
|
self.load_url(top_level_browsing_context_id, pipeline_id, load_data, false);
|
||||||
load_data,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
FromCompositorMsg::IsReadyToSaveImage(pipeline_states) => {
|
FromCompositorMsg::IsReadyToSaveImage(pipeline_states) => {
|
||||||
let is_ready = self.handle_is_ready_to_save_image(pipeline_states);
|
let is_ready = self.handle_is_ready_to_save_image(pipeline_states);
|
||||||
|
@ -1176,11 +1210,9 @@ where
|
||||||
FromScriptMsg::ChangeRunningAnimationsState(animation_state) => {
|
FromScriptMsg::ChangeRunningAnimationsState(animation_state) => {
|
||||||
self.handle_change_running_animations_state(source_pipeline_id, animation_state)
|
self.handle_change_running_animations_state(source_pipeline_id, animation_state)
|
||||||
},
|
},
|
||||||
// Load a new page from a mouse click
|
// Ask the embedder for permission to load a new page.
|
||||||
// If there is already a pending page (self.pending_changes), it will not be overridden;
|
|
||||||
// However, if the id is not encompassed by another change, it will be.
|
|
||||||
FromScriptMsg::LoadUrl(load_data, replace) => {
|
FromScriptMsg::LoadUrl(load_data, replace) => {
|
||||||
self.handle_load_url_msg(source_top_ctx_id, source_pipeline_id, load_data, replace);
|
self.schedule_navigation(source_top_ctx_id, source_pipeline_id, load_data, replace);
|
||||||
},
|
},
|
||||||
FromScriptMsg::AbortLoadUrl => {
|
FromScriptMsg::AbortLoadUrl => {
|
||||||
self.handle_abort_load_url_msg(source_pipeline_id);
|
self.handle_abort_load_url_msg(source_pipeline_id);
|
||||||
|
@ -2115,14 +2147,33 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_load_url_msg(
|
/// Schedule a navigation(via load_url).
|
||||||
|
/// 1: Ask the embedder for permission.
|
||||||
|
/// 2: Store the details of the navigation, pending approval from the embedder.
|
||||||
|
fn schedule_navigation(
|
||||||
&mut self,
|
&mut self,
|
||||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||||
source_id: PipelineId,
|
source_id: PipelineId,
|
||||||
load_data: LoadData,
|
load_data: LoadData,
|
||||||
replace: bool,
|
replace: bool,
|
||||||
) {
|
) {
|
||||||
self.load_url(top_level_browsing_context_id, source_id, load_data, replace);
|
match self.pending_approval_navigations.entry(source_id) {
|
||||||
|
Entry::Occupied(_) => {
|
||||||
|
return warn!(
|
||||||
|
"Pipeline {:?} tried to schedule a navigation while one is already pending.",
|
||||||
|
source_id
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
let _ = entry.insert((load_data.clone(), replace));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// Allow the embedder to handle the url itself
|
||||||
|
let msg = (
|
||||||
|
Some(top_level_browsing_context_id),
|
||||||
|
EmbedderMsg::AllowNavigationRequest(source_id, load_data.url.clone()),
|
||||||
|
);
|
||||||
|
self.embedder_proxy.send(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_url(
|
fn load_url(
|
||||||
|
@ -2132,17 +2183,6 @@ where
|
||||||
load_data: LoadData,
|
load_data: LoadData,
|
||||||
replace: bool,
|
replace: bool,
|
||||||
) -> Option<PipelineId> {
|
) -> Option<PipelineId> {
|
||||||
// Allow the embedder to handle the url itself
|
|
||||||
let (chan, port) = ipc::channel().expect("Failed to create IPC channel!");
|
|
||||||
let msg = (
|
|
||||||
Some(top_level_browsing_context_id),
|
|
||||||
EmbedderMsg::AllowNavigation(load_data.url.clone(), chan),
|
|
||||||
);
|
|
||||||
self.embedder_proxy.send(msg);
|
|
||||||
if let Ok(false) = port.recv() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("Loading {} in pipeline {}.", load_data.url, source_id);
|
debug!("Loading {} in pipeline {}.", load_data.url, source_id);
|
||||||
// If this load targets an iframe, its framing element may exist
|
// If this load targets an iframe, its framing element may exist
|
||||||
// in a separate script thread than the framed document that initiated
|
// in a separate script thread than the framed document that initiated
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub mod resources;
|
||||||
use crossbeam_channel::{Receiver, Sender};
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use keyboard_types::KeyboardEvent;
|
use keyboard_types::KeyboardEvent;
|
||||||
use msg::constellation_msg::{InputMethodType, TopLevelBrowsingContextId};
|
use msg::constellation_msg::{InputMethodType, PipelineId, TopLevelBrowsingContextId};
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::fmt::{Debug, Error, Formatter};
|
use std::fmt::{Debug, Error, Formatter};
|
||||||
use style_traits::cursor::CursorKind;
|
use style_traits::cursor::CursorKind;
|
||||||
|
@ -79,8 +79,8 @@ pub enum EmbedderMsg {
|
||||||
ResizeTo(DeviceIntSize),
|
ResizeTo(DeviceIntSize),
|
||||||
// Show an alert message.
|
// Show an alert message.
|
||||||
Alert(String, IpcSender<()>),
|
Alert(String, IpcSender<()>),
|
||||||
/// Wether or not to follow a link
|
/// Wether or not to allow a pipeline to load a url.
|
||||||
AllowNavigation(ServoUrl, IpcSender<bool>),
|
AllowNavigationRequest(PipelineId, ServoUrl),
|
||||||
/// Whether or not to allow script to open a new tab/browser
|
/// Whether or not to allow script to open a new tab/browser
|
||||||
AllowOpeningBrowser(IpcSender<bool>),
|
AllowOpeningBrowser(IpcSender<bool>),
|
||||||
/// A new browser was created by script
|
/// A new browser was created by script
|
||||||
|
@ -128,7 +128,7 @@ impl Debug for EmbedderMsg {
|
||||||
EmbedderMsg::ResizeTo(..) => write!(f, "ResizeTo"),
|
EmbedderMsg::ResizeTo(..) => write!(f, "ResizeTo"),
|
||||||
EmbedderMsg::Alert(..) => write!(f, "Alert"),
|
EmbedderMsg::Alert(..) => write!(f, "Alert"),
|
||||||
EmbedderMsg::AllowUnload(..) => write!(f, "AllowUnload"),
|
EmbedderMsg::AllowUnload(..) => write!(f, "AllowUnload"),
|
||||||
EmbedderMsg::AllowNavigation(..) => write!(f, "AllowNavigation"),
|
EmbedderMsg::AllowNavigationRequest(..) => write!(f, "AllowNavigationRequest"),
|
||||||
EmbedderMsg::Keyboard(..) => write!(f, "Keyboard"),
|
EmbedderMsg::Keyboard(..) => write!(f, "Keyboard"),
|
||||||
EmbedderMsg::SetCursor(..) => write!(f, "SetCursor"),
|
EmbedderMsg::SetCursor(..) => write!(f, "SetCursor"),
|
||||||
EmbedderMsg::NewFavicon(..) => write!(f, "NewFavicon"),
|
EmbedderMsg::NewFavicon(..) => write!(f, "NewFavicon"),
|
||||||
|
|
|
@ -726,6 +726,8 @@ pub enum ConstellationMsg {
|
||||||
IsReadyToSaveImage(HashMap<PipelineId, Epoch>),
|
IsReadyToSaveImage(HashMap<PipelineId, Epoch>),
|
||||||
/// Inform the constellation of a key event.
|
/// Inform the constellation of a key event.
|
||||||
Keyboard(KeyboardEvent),
|
Keyboard(KeyboardEvent),
|
||||||
|
/// Whether to allow script to navigate.
|
||||||
|
AllowNavigationResponse(PipelineId, bool),
|
||||||
/// Request to load a page.
|
/// Request to load a page.
|
||||||
LoadUrl(TopLevelBrowsingContextId, ServoUrl),
|
LoadUrl(TopLevelBrowsingContextId, ServoUrl),
|
||||||
/// Request to traverse the joint session history of the provided browsing context.
|
/// Request to traverse the joint session history of the provided browsing context.
|
||||||
|
@ -770,6 +772,7 @@ impl fmt::Debug for ConstellationMsg {
|
||||||
GetFocusTopLevelBrowsingContext(..) => "GetFocusTopLevelBrowsingContext",
|
GetFocusTopLevelBrowsingContext(..) => "GetFocusTopLevelBrowsingContext",
|
||||||
IsReadyToSaveImage(..) => "IsReadyToSaveImage",
|
IsReadyToSaveImage(..) => "IsReadyToSaveImage",
|
||||||
Keyboard(..) => "Keyboard",
|
Keyboard(..) => "Keyboard",
|
||||||
|
AllowNavigationResponse(..) => "AllowNavigationResponse",
|
||||||
LoadUrl(..) => "LoadUrl",
|
LoadUrl(..) => "LoadUrl",
|
||||||
TraverseHistory(..) => "TraverseHistory",
|
TraverseHistory(..) => "TraverseHistory",
|
||||||
WindowSize(..) => "WindowSize",
|
WindowSize(..) => "WindowSize",
|
||||||
|
|
|
@ -267,6 +267,16 @@ where
|
||||||
self.compositor.on_resize_window_event();
|
self.compositor.on_resize_window_event();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
WindowEvent::AllowNavigationResponse(pipeline_id, allowed) => {
|
||||||
|
let msg = ConstellationMsg::AllowNavigationResponse(pipeline_id, allowed);
|
||||||
|
if let Err(e) = self.constellation_chan.send(msg) {
|
||||||
|
warn!(
|
||||||
|
"Sending allow navigation to constellation failed ({:?}).",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
WindowEvent::LoadUrl(top_level_browsing_context_id, url) => {
|
WindowEvent::LoadUrl(top_level_browsing_context_id, url) => {
|
||||||
let msg = ConstellationMsg::LoadUrl(top_level_browsing_context_id, url);
|
let msg = ConstellationMsg::LoadUrl(top_level_browsing_context_id, url);
|
||||||
if let Err(e) = self.constellation_chan.send(msg) {
|
if let Err(e) = self.constellation_chan.send(msg) {
|
||||||
|
|
|
@ -128,12 +128,15 @@ pub unsafe extern "C" fn heartbeat_servo(servo: *mut ServoInstance) {
|
||||||
// Servo heartbeat goes here!
|
// Servo heartbeat goes here!
|
||||||
if let Some(servo) = servo.as_mut() {
|
if let Some(servo) = servo.as_mut() {
|
||||||
servo.servo.handle_events(vec![]);
|
servo.servo.handle_events(vec![]);
|
||||||
for ((_browser_id, event)) in servo.servo.get_events() {
|
for ((browser_id, event)) in servo.servo.get_events() {
|
||||||
match event {
|
match event {
|
||||||
// Respond to any messages with a response channel
|
// Respond to any messages with a response channel
|
||||||
// to avoid deadlocking the constellation
|
// to avoid deadlocking the constellation
|
||||||
EmbedderMsg::AllowNavigation(_url, sender) => {
|
EmbedderMsg::AllowNavigationRequest(pipeline_id, _url) => {
|
||||||
let _ = sender.send(true);
|
if let Some(_browser_id) = browser_id {
|
||||||
|
let window_event = WindowEvent::AllowNavigationResponse(pipeline_id, true);
|
||||||
|
servo.servo.handle_events(vec![window_event]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
EmbedderMsg::GetSelectedBluetoothDevice(_, sender) => {
|
EmbedderMsg::GetSelectedBluetoothDevice(_, sender) => {
|
||||||
let _ = sender.send(None);
|
let _ = sender.send(None);
|
||||||
|
|
|
@ -388,7 +388,7 @@ impl ServoGlue {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_servo_events(&mut self) -> Result<(), &'static str> {
|
fn handle_servo_events(&mut self) -> Result<(), &'static str> {
|
||||||
for (_browser_id, event) in self.servo.get_events() {
|
for (browser_id, event) in self.servo.get_events() {
|
||||||
match event {
|
match event {
|
||||||
EmbedderMsg::ChangePageTitle(title) => {
|
EmbedderMsg::ChangePageTitle(title) => {
|
||||||
let fallback_title: String = if let Some(ref current_url) = self.current_url {
|
let fallback_title: String = if let Some(ref current_url) = self.current_url {
|
||||||
|
@ -403,10 +403,11 @@ impl ServoGlue {
|
||||||
let title = format!("{} - Servo", title);
|
let title = format!("{} - Servo", title);
|
||||||
self.callbacks.host_callbacks.on_title_changed(title);
|
self.callbacks.host_callbacks.on_title_changed(title);
|
||||||
},
|
},
|
||||||
EmbedderMsg::AllowNavigation(_url, response_chan) => {
|
EmbedderMsg::AllowNavigationRequest(pipeline_id, _url) => {
|
||||||
if let Err(e) = response_chan.send(true) {
|
if let Some(_browser_id) = browser_id {
|
||||||
warn!("Failed to send allow_navigation() response: {}", e);
|
let window_event = WindowEvent::AllowNavigationResponse(pipeline_id, true);
|
||||||
};
|
let _ = self.process_event(window_event);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
EmbedderMsg::HistoryChanged(entries, current) => {
|
EmbedderMsg::HistoryChanged(entries, current) => {
|
||||||
let can_go_back = current > 0;
|
let can_go_back = current > 0;
|
||||||
|
|
|
@ -290,9 +290,10 @@ impl Browser {
|
||||||
.push(WindowEvent::SendError(browser_id, reason));
|
.push(WindowEvent::SendError(browser_id, reason));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
EmbedderMsg::AllowNavigation(_url, sender) => {
|
EmbedderMsg::AllowNavigationRequest(pipeline_id, _url) => {
|
||||||
if let Err(e) = sender.send(true) {
|
if let Some(_browser_id) = browser_id {
|
||||||
warn!("Failed to send AllowNavigation response: {}", e);
|
self.event_queue
|
||||||
|
.push(WindowEvent::AllowNavigationResponse(pipeline_id, true));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
EmbedderMsg::AllowOpeningBrowser(response_chan) => {
|
EmbedderMsg::AllowOpeningBrowser(response_chan) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue