mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
constellation: focusing and closing webviews (#30842)
* constellation: focusing, closing, and native window visibility * rename “browser” to “webview”, “unfocus” to “blur” * remove native window visibility from constellation * rename more “browser” to “webview” * guard clauses * don’t automatically focus when no webviews are focused * comment spec steps for window.close() * use format interpolation Co-authored-by: Martin Robinson <mrobinson@igalia.com> * fix formatting * rename “Webview” to “WebView” in types and type parameters * remove unused method * fix libsimpleservo --------- Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
6baaa82826
commit
eb95703325
16 changed files with 533 additions and 285 deletions
|
@ -83,14 +83,14 @@ pub enum EmbedderEvent {
|
|||
/// Sent when Ctr+R/Apple+R is called to reload the current page.
|
||||
Reload(TopLevelBrowsingContextId),
|
||||
/// Create a new top level browsing context
|
||||
NewBrowser(ServoUrl, TopLevelBrowsingContextId),
|
||||
NewWebView(ServoUrl, TopLevelBrowsingContextId),
|
||||
/// Close a top level browsing context
|
||||
CloseBrowser(TopLevelBrowsingContextId),
|
||||
CloseWebView(TopLevelBrowsingContextId),
|
||||
/// Panic a top level browsing context.
|
||||
SendError(Option<TopLevelBrowsingContextId>, String),
|
||||
/// Make a top level browsing context visible, hiding the previous
|
||||
/// visible one.
|
||||
SelectBrowser(TopLevelBrowsingContextId),
|
||||
FocusWebView(TopLevelBrowsingContextId),
|
||||
/// Toggles a debug flag in WebRender
|
||||
ToggleWebRenderDebug(WebRenderDebugOption),
|
||||
/// Capture current WebRender
|
||||
|
@ -102,8 +102,8 @@ pub enum EmbedderEvent {
|
|||
/// Sent when the user triggers a media action through the UA exposed media UI
|
||||
/// (play, pause, seek, etc.).
|
||||
MediaSessionAction(MediaSessionActionType),
|
||||
/// Set browser visibility. A hidden browser will not tick the animations.
|
||||
ChangeBrowserVisibility(TopLevelBrowsingContextId, bool),
|
||||
/// The visibility of the webview has changed.
|
||||
WebViewVisibilityChanged(TopLevelBrowsingContextId, bool),
|
||||
/// Virtual keyboard was dismissed
|
||||
IMEDismissed,
|
||||
/// Sent on platforms like Android where the native widget surface can be
|
||||
|
@ -136,16 +136,16 @@ impl Debug for EmbedderEvent {
|
|||
EmbedderEvent::Navigation(..) => write!(f, "Navigation"),
|
||||
EmbedderEvent::Quit => write!(f, "Quit"),
|
||||
EmbedderEvent::Reload(..) => write!(f, "Reload"),
|
||||
EmbedderEvent::NewBrowser(..) => write!(f, "NewBrowser"),
|
||||
EmbedderEvent::NewWebView(..) => write!(f, "NewWebView"),
|
||||
EmbedderEvent::SendError(..) => write!(f, "SendError"),
|
||||
EmbedderEvent::CloseBrowser(..) => write!(f, "CloseBrowser"),
|
||||
EmbedderEvent::SelectBrowser(..) => write!(f, "SelectBrowser"),
|
||||
EmbedderEvent::CloseWebView(..) => write!(f, "CloseWebView"),
|
||||
EmbedderEvent::FocusWebView(..) => write!(f, "FocusWebView"),
|
||||
EmbedderEvent::ToggleWebRenderDebug(..) => write!(f, "ToggleWebRenderDebug"),
|
||||
EmbedderEvent::CaptureWebRender => write!(f, "CaptureWebRender"),
|
||||
EmbedderEvent::ToggleSamplingProfiler(..) => write!(f, "ToggleSamplingProfiler"),
|
||||
EmbedderEvent::ExitFullScreen(..) => write!(f, "ExitFullScreen"),
|
||||
EmbedderEvent::MediaSessionAction(..) => write!(f, "MediaSessionAction"),
|
||||
EmbedderEvent::ChangeBrowserVisibility(..) => write!(f, "ChangeBrowserVisibility"),
|
||||
EmbedderEvent::WebViewVisibilityChanged(..) => write!(f, "WebViewVisibilityChanged"),
|
||||
EmbedderEvent::IMEDismissed => write!(f, "IMEDismissed"),
|
||||
EmbedderEvent::ClearCache => write!(f, "ClearCache"),
|
||||
EmbedderEvent::InvalidateNativeSurface => write!(f, "InvalidateNativeSurface"),
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
//! to a forest whose roots are top-level browsing context. The logical
|
||||
//! relationship between these types is:
|
||||
//!
|
||||
//! ```
|
||||
//! ```text
|
||||
//! +------------+ +------------+ +---------+
|
||||
//! | Browsing | ------parent?------> | Pipeline | --event_loop--> | Event |
|
||||
//! | Context | ------current------> | | | Loop |
|
||||
|
@ -172,6 +172,7 @@ use crate::session_history::{
|
|||
JointSessionHistory, NeedsToReload, SessionHistoryChange, SessionHistoryDiff,
|
||||
};
|
||||
use crate::timer_scheduler::TimerScheduler;
|
||||
use crate::webview::WebViewManager;
|
||||
|
||||
type PendingApprovalNavigations = HashMap<PipelineId, (LoadData, HistoryEntryReplacement)>;
|
||||
|
||||
|
@ -223,15 +224,15 @@ struct WebrenderWGPU {
|
|||
wgpu_image_map: Arc<Mutex<HashMap<u64, webgpu::PresentationData>>>,
|
||||
}
|
||||
|
||||
/// Servo supports tabs (referred to as browsers), so `Constellation` needs to
|
||||
/// store browser specific data for bookkeeping.
|
||||
struct Browser {
|
||||
/// The currently focused browsing context in this browser for key events.
|
||||
/// Servo supports multiple top-level browsing contexts or “webviews”, so `Constellation` needs to
|
||||
/// store webview-specific data for bookkeeping.
|
||||
struct WebView {
|
||||
/// The currently focused browsing context in this webview for key events.
|
||||
/// The focused pipeline is the current entry of the focused browsing
|
||||
/// context.
|
||||
focused_browsing_context_id: BrowsingContextId,
|
||||
|
||||
/// The joint session history for this browser.
|
||||
/// The joint session history for this webview.
|
||||
session_history: JointSessionHistory,
|
||||
}
|
||||
|
||||
|
@ -325,13 +326,8 @@ pub struct Constellation<Message, LTF, STF, SWF> {
|
|||
/// constellation to send messages to the compositor thread.
|
||||
compositor_proxy: CompositorProxy,
|
||||
|
||||
/// The last frame tree sent to WebRender, denoting the browser (tab) user
|
||||
/// has currently selected. This also serves as the key to retrieve data
|
||||
/// about the current active browser from `browsers`.
|
||||
active_browser_id: Option<TopLevelBrowsingContextId>,
|
||||
|
||||
/// Bookkeeping data for all browsers in constellation.
|
||||
browsers: HashMap<TopLevelBrowsingContextId, Browser>,
|
||||
/// Bookkeeping data for all webviews in the constellation.
|
||||
webviews: WebViewManager<WebView>,
|
||||
|
||||
/// Channels for the constellation to send messages to the public
|
||||
/// resource-related threads. There are two groups of resource threads: one
|
||||
|
@ -758,8 +754,7 @@ where
|
|||
network_listener_receiver: network_listener_receiver,
|
||||
embedder_proxy: state.embedder_proxy,
|
||||
compositor_proxy: state.compositor_proxy,
|
||||
active_browser_id: None,
|
||||
browsers: HashMap::new(),
|
||||
webviews: WebViewManager::default(),
|
||||
devtools_sender: state.devtools_sender,
|
||||
bluetooth_ipc_sender: state.bluetooth_thread,
|
||||
public_resource_threads: state.public_resource_threads,
|
||||
|
@ -1352,9 +1347,7 @@ where
|
|||
self.handle_get_pipeline(browsing_context_id, response_sender);
|
||||
},
|
||||
FromCompositorMsg::GetFocusTopLevelBrowsingContext(resp_chan) => {
|
||||
// The focused browsing context's top-level browsing context is
|
||||
// the active browser's id itself.
|
||||
let _ = resp_chan.send(self.active_browser_id);
|
||||
let _ = resp_chan.send(self.webviews.focused_webview().map(|(id, _)| id));
|
||||
},
|
||||
FromCompositorMsg::Keyboard(key_event) => {
|
||||
self.handle_key_msg(key_event);
|
||||
|
@ -1468,11 +1461,11 @@ where
|
|||
},
|
||||
// Create a new top level browsing context. Will use response_chan to return
|
||||
// the browsing context id.
|
||||
FromCompositorMsg::NewBrowser(url, top_level_browsing_context_id) => {
|
||||
FromCompositorMsg::NewWebView(url, top_level_browsing_context_id) => {
|
||||
self.handle_new_top_level_browsing_context(url, top_level_browsing_context_id);
|
||||
},
|
||||
// Close a top level browsing context.
|
||||
FromCompositorMsg::CloseBrowser(top_level_browsing_context_id) => {
|
||||
FromCompositorMsg::CloseWebView(top_level_browsing_context_id) => {
|
||||
self.handle_close_top_level_browsing_context(top_level_browsing_context_id);
|
||||
},
|
||||
// Panic a top level browsing context.
|
||||
|
@ -1483,9 +1476,23 @@ where
|
|||
}
|
||||
self.handle_panic(top_level_browsing_context_id, error, None);
|
||||
},
|
||||
// Send frame tree to WebRender. Make it visible.
|
||||
FromCompositorMsg::SelectBrowser(top_level_browsing_context_id) => {
|
||||
self.send_frame_tree(top_level_browsing_context_id);
|
||||
FromCompositorMsg::FocusWebView(top_level_browsing_context_id) => {
|
||||
if self.webviews.get(top_level_browsing_context_id).is_none() {
|
||||
return warn!("{top_level_browsing_context_id}: FocusWebView on unknown top-level browsing context");
|
||||
}
|
||||
self.webviews.focus(top_level_browsing_context_id);
|
||||
self.embedder_proxy.send((
|
||||
Some(top_level_browsing_context_id),
|
||||
EmbedderMsg::WebViewFocused(top_level_browsing_context_id),
|
||||
));
|
||||
if !cfg!(feature = "multiview") {
|
||||
self.update_frame_tree_if_focused(top_level_browsing_context_id);
|
||||
}
|
||||
},
|
||||
FromCompositorMsg::BlurWebView => {
|
||||
self.webviews.unfocus();
|
||||
self.embedder_proxy
|
||||
.send((None, EmbedderMsg::WebViewBlurred));
|
||||
},
|
||||
// Handle a forward or back request
|
||||
FromCompositorMsg::TraverseHistory(top_level_browsing_context_id, direction) => {
|
||||
|
@ -1534,8 +1541,8 @@ where
|
|||
FromCompositorMsg::MediaSessionAction(action) => {
|
||||
self.handle_media_session_action_msg(action);
|
||||
},
|
||||
FromCompositorMsg::ChangeBrowserVisibility(top_level_browsing_context_id, visible) => {
|
||||
self.handle_change_browser_visibility(top_level_browsing_context_id, visible);
|
||||
FromCompositorMsg::WebViewVisibilityChanged(webview_id, visible) => {
|
||||
self.notify_webview_visibility(webview_id, visible);
|
||||
},
|
||||
FromCompositorMsg::ReadyToPresent(top_level_browsing_context_id) => {
|
||||
self.embedder_proxy.send((
|
||||
|
@ -2934,7 +2941,7 @@ where
|
|||
let pipeline_id = PipelineId::new();
|
||||
let msg = (
|
||||
Some(top_level_browsing_context_id),
|
||||
EmbedderMsg::BrowserCreated(top_level_browsing_context_id),
|
||||
EmbedderMsg::WebViewOpened(top_level_browsing_context_id),
|
||||
);
|
||||
self.embedder_proxy.send(msg);
|
||||
let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id);
|
||||
|
@ -2950,11 +2957,11 @@ where
|
|||
let is_private = false;
|
||||
let is_visible = true;
|
||||
|
||||
// Register this new top-level browsing context id as a browser and set
|
||||
// Register this new top-level browsing context id as a webview and set
|
||||
// its focused browsing context to be itself.
|
||||
self.browsers.insert(
|
||||
self.webviews.add(
|
||||
top_level_browsing_context_id,
|
||||
Browser {
|
||||
WebView {
|
||||
focused_browsing_context_id: browsing_context_id,
|
||||
session_history: JointSessionHistory::new(),
|
||||
},
|
||||
|
@ -3000,22 +3007,45 @@ where
|
|||
&mut self,
|
||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
) {
|
||||
debug!("{top_level_browsing_context_id}: Closing");
|
||||
let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id);
|
||||
self.close_browsing_context(browsing_context_id, ExitPipelineMode::Normal);
|
||||
self.browsers.remove(&top_level_browsing_context_id);
|
||||
if self.active_browser_id == Some(top_level_browsing_context_id) {
|
||||
self.active_browser_id = None;
|
||||
let browsing_context =
|
||||
self.close_browsing_context(browsing_context_id, ExitPipelineMode::Normal);
|
||||
if self.webviews.focused_webview().map(|(id, _)| id) == Some(top_level_browsing_context_id)
|
||||
{
|
||||
self.embedder_proxy
|
||||
.send((None, EmbedderMsg::WebViewBlurred));
|
||||
}
|
||||
let browsing_context = match self.browsing_contexts.get(&browsing_context_id) {
|
||||
Some(bc) => bc,
|
||||
None => {
|
||||
warn!("Browsing context closed before it started");
|
||||
return;
|
||||
},
|
||||
self.webviews.remove(top_level_browsing_context_id);
|
||||
// TODO Send the compositor a RemoveWebView event.
|
||||
self.embedder_proxy.send((
|
||||
Some(top_level_browsing_context_id),
|
||||
EmbedderMsg::WebViewClosed(top_level_browsing_context_id),
|
||||
));
|
||||
|
||||
let Some(browsing_context) = browsing_context else {
|
||||
return;
|
||||
};
|
||||
// https://html.spec.whatwg.org/multipage/#bcg-remove
|
||||
self.browsing_context_group_set
|
||||
.remove(&browsing_context.bc_group_id);
|
||||
let bc_group_id = browsing_context.bc_group_id;
|
||||
let Some(bc_group) = self.browsing_context_group_set.get_mut(&bc_group_id) else {
|
||||
warn!("{}: Browsing context group not found!", bc_group_id);
|
||||
return;
|
||||
};
|
||||
if !bc_group
|
||||
.top_level_browsing_context_set
|
||||
.remove(&top_level_browsing_context_id)
|
||||
{
|
||||
warn!(
|
||||
"{top_level_browsing_context_id}: Top-level browsing context not found in {bc_group_id}",
|
||||
);
|
||||
}
|
||||
if bc_group.top_level_browsing_context_set.is_empty() {
|
||||
self.browsing_context_group_set
|
||||
.remove(&browsing_context.bc_group_id);
|
||||
}
|
||||
|
||||
debug!("{top_level_browsing_context_id}: Closed");
|
||||
}
|
||||
|
||||
fn handle_iframe_size_msg(&mut self, iframe_sizes: Vec<IFrameSizeMsg>) {
|
||||
|
@ -3312,9 +3342,9 @@ where
|
|||
|
||||
assert!(!self.pipelines.contains_key(&new_pipeline_id));
|
||||
self.pipelines.insert(new_pipeline_id, pipeline);
|
||||
self.browsers.insert(
|
||||
self.webviews.add(
|
||||
new_top_level_browsing_context_id,
|
||||
Browser {
|
||||
WebView {
|
||||
focused_browsing_context_id: new_browsing_context_id,
|
||||
session_history: JointSessionHistory::new(),
|
||||
},
|
||||
|
@ -3785,7 +3815,7 @@ where
|
|||
self.notify_history_changed(top_level_browsing_context_id);
|
||||
|
||||
self.trim_history(top_level_browsing_context_id);
|
||||
self.update_frame_tree_if_active(top_level_browsing_context_id);
|
||||
self.update_frame_tree_if_focused(top_level_browsing_context_id);
|
||||
}
|
||||
|
||||
fn update_browsing_context(
|
||||
|
@ -3944,9 +3974,9 @@ where
|
|||
response_sender: IpcSender<u32>,
|
||||
) {
|
||||
let length = self
|
||||
.browsers
|
||||
.get(&top_level_browsing_context_id)
|
||||
.map(|browser| browser.session_history.history_length())
|
||||
.webviews
|
||||
.get(top_level_browsing_context_id)
|
||||
.map(|webview| webview.session_history.history_length())
|
||||
.unwrap_or(1);
|
||||
let _ = response_sender.send(length as u32);
|
||||
}
|
||||
|
@ -4017,9 +4047,9 @@ where
|
|||
fn handle_ime_dismissed(&mut self) {
|
||||
// Send to the focused browsing contexts' current pipeline.
|
||||
let focused_browsing_context_id = self
|
||||
.active_browser_id
|
||||
.and_then(|browser_id| self.browsers.get(&browser_id))
|
||||
.map(|browser| browser.focused_browsing_context_id);
|
||||
.webviews
|
||||
.focused_webview()
|
||||
.map(|(_, webview)| webview.focused_browsing_context_id);
|
||||
if let Some(browsing_context_id) = focused_browsing_context_id {
|
||||
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
|
||||
Some(ctx) => ctx.pipeline_id,
|
||||
|
@ -4048,9 +4078,9 @@ where
|
|||
// Send to the focused browsing contexts' current pipeline. If it
|
||||
// doesn't exist, fall back to sending to the compositor.
|
||||
let focused_browsing_context_id = self
|
||||
.active_browser_id
|
||||
.and_then(|browser_id| self.browsers.get(&browser_id))
|
||||
.map(|browser| browser.focused_browsing_context_id);
|
||||
.webviews
|
||||
.focused_webview()
|
||||
.map(|(_, webview)| webview.focused_browsing_context_id);
|
||||
match focused_browsing_context_id {
|
||||
Some(browsing_context_id) => {
|
||||
let event = CompositorEvent::KeyboardEvent(event);
|
||||
|
@ -4075,6 +4105,7 @@ where
|
|||
}
|
||||
},
|
||||
None => {
|
||||
warn!("No focused browsing context! Falling back to sending key to compositor");
|
||||
let event = (None, EmbedderMsg::Keyboard(event));
|
||||
self.embedder_proxy.send(event);
|
||||
},
|
||||
|
@ -4182,10 +4213,17 @@ where
|
|||
None => return warn!("{}: Focus parent after closure", pipeline_id),
|
||||
};
|
||||
|
||||
// Update the focused browsing context in its browser in `browsers`.
|
||||
match self.browsers.get_mut(&top_level_browsing_context_id) {
|
||||
Some(browser) => {
|
||||
browser.focused_browsing_context_id = browsing_context_id;
|
||||
// Focus the top-level browsing context.
|
||||
self.webviews.focus(top_level_browsing_context_id);
|
||||
self.embedder_proxy.send((
|
||||
Some(top_level_browsing_context_id),
|
||||
EmbedderMsg::WebViewFocused(top_level_browsing_context_id),
|
||||
));
|
||||
|
||||
// Update the webview’s focused browsing context.
|
||||
match self.webviews.get_mut(top_level_browsing_context_id) {
|
||||
Some(webview) => {
|
||||
webview.focused_browsing_context_id = browsing_context_id;
|
||||
},
|
||||
None => {
|
||||
return warn!(
|
||||
|
@ -4428,7 +4466,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_change_browser_visibility(
|
||||
fn notify_webview_visibility(
|
||||
&mut self,
|
||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
visible: bool,
|
||||
|
@ -4437,14 +4475,11 @@ where
|
|||
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
|
||||
Some(browsing_context) => browsing_context.pipeline_id,
|
||||
None => {
|
||||
return warn!(
|
||||
"{}: Got visibility change event after closure",
|
||||
browsing_context_id
|
||||
);
|
||||
return warn!("{browsing_context_id}: Tried to notify visibility after closure");
|
||||
},
|
||||
};
|
||||
match self.pipelines.get(&pipeline_id) {
|
||||
None => return warn!("{}: Got visibility change event after closure", pipeline_id),
|
||||
None => return warn!("{pipeline_id}: Tried to notify visibility after closure"),
|
||||
Some(pipeline) => pipeline.notify_visibility(visible),
|
||||
};
|
||||
}
|
||||
|
@ -4456,8 +4491,8 @@ where
|
|||
// LoadData of inner frames are ignored and replaced with the LoadData
|
||||
// of the parent.
|
||||
|
||||
let session_history = match self.browsers.get(&top_level_browsing_context_id) {
|
||||
Some(browser) => &browser.session_history,
|
||||
let session_history = match self.webviews.get(top_level_browsing_context_id) {
|
||||
Some(webview) => &webview.session_history,
|
||||
None => {
|
||||
return warn!(
|
||||
"{}: Session history does not exist for browsing context",
|
||||
|
@ -4602,11 +4637,9 @@ where
|
|||
// context in which the page is being loaded, then update the focused
|
||||
// browsing context to be the one where the page is being loaded.
|
||||
if self.focused_browsing_context_is_descendant_of(change.browsing_context_id) {
|
||||
self.browsers
|
||||
.entry(change.top_level_browsing_context_id)
|
||||
.and_modify(|browser| {
|
||||
browser.focused_browsing_context_id = change.browsing_context_id
|
||||
});
|
||||
if let Some(webview) = self.webviews.get_mut(change.top_level_browsing_context_id) {
|
||||
webview.focused_browsing_context_id = change.browsing_context_id;
|
||||
}
|
||||
}
|
||||
|
||||
let (old_pipeline_id, top_level_id) =
|
||||
|
@ -4745,7 +4778,7 @@ where
|
|||
}
|
||||
|
||||
self.notify_history_changed(change.top_level_browsing_context_id);
|
||||
self.update_frame_tree_if_active(change.top_level_browsing_context_id);
|
||||
self.update_frame_tree_if_focused(change.top_level_browsing_context_id);
|
||||
}
|
||||
|
||||
fn focused_browsing_context_is_descendant_of(
|
||||
|
@ -4753,9 +4786,9 @@ where
|
|||
browsing_context_id: BrowsingContextId,
|
||||
) -> bool {
|
||||
let focused_browsing_context_id = self
|
||||
.active_browser_id
|
||||
.and_then(|browser_id| self.browsers.get(&browser_id))
|
||||
.map(|browser| browser.focused_browsing_context_id);
|
||||
.webviews
|
||||
.focused_webview()
|
||||
.map(|(_, webview)| webview.focused_browsing_context_id);
|
||||
focused_browsing_context_id.map_or(false, |focus_ctx_id| {
|
||||
focus_ctx_id == browsing_context_id ||
|
||||
self.fully_active_descendant_browsing_contexts_iter(browsing_context_id)
|
||||
|
@ -4912,10 +4945,8 @@ where
|
|||
//
|
||||
// If there is no focus browsing context yet, the initial page has
|
||||
// not loaded, so there is nothing to save yet.
|
||||
let top_level_browsing_context_id = match self.active_browser_id {
|
||||
Some(id) => id,
|
||||
None => return ReadyToSave::NoTopLevelBrowsingContext,
|
||||
};
|
||||
let Some(top_level_browsing_context_id) = self.webviews.focused_webview().map(|(id, _)| id)
|
||||
else { return ReadyToSave::NoTopLevelBrowsingContext };
|
||||
|
||||
// If there are pending loads, wait for those to complete.
|
||||
if !self.pending_changes.is_empty() {
|
||||
|
@ -5141,12 +5172,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// Close a browsing context (and all children)
|
||||
// Close and return the browsing context with the given id (and its children), if it exists.
|
||||
fn close_browsing_context(
|
||||
&mut self,
|
||||
browsing_context_id: BrowsingContextId,
|
||||
exit_mode: ExitPipelineMode,
|
||||
) {
|
||||
) -> Option<BrowsingContext> {
|
||||
debug!("{}: Closing", browsing_context_id);
|
||||
|
||||
self.close_browsing_context_children(
|
||||
|
@ -5157,7 +5188,10 @@ where
|
|||
|
||||
let browsing_context = match self.browsing_contexts.remove(&browsing_context_id) {
|
||||
Some(ctx) => ctx,
|
||||
None => return warn!("{}: Closing twice", browsing_context_id),
|
||||
None => {
|
||||
warn!("{browsing_context_id}: Closing twice");
|
||||
return None;
|
||||
},
|
||||
};
|
||||
|
||||
{
|
||||
|
@ -5168,12 +5202,13 @@ where
|
|||
if let Some(parent_pipeline_id) = browsing_context.parent_pipeline_id {
|
||||
match self.pipelines.get_mut(&parent_pipeline_id) {
|
||||
None => {
|
||||
return warn!("{}: Child closed after parent", parent_pipeline_id);
|
||||
warn!("{parent_pipeline_id}: Child closed after parent");
|
||||
},
|
||||
Some(parent_pipeline) => parent_pipeline.remove_child(browsing_context_id),
|
||||
};
|
||||
}
|
||||
debug!("{}: Closed", browsing_context_id);
|
||||
Some(browsing_context)
|
||||
}
|
||||
|
||||
// Close the children of a browsing context
|
||||
|
@ -5212,13 +5247,13 @@ where
|
|||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
pipeline_id: PipelineId,
|
||||
) {
|
||||
match self.browsers.get_mut(&top_level_browsing_context_id) {
|
||||
Some(browser) => {
|
||||
match self.webviews.get_mut(top_level_browsing_context_id) {
|
||||
Some(webview) => {
|
||||
let load_data = match self.pipelines.get(&pipeline_id) {
|
||||
Some(pipeline) => pipeline.load_data.clone(),
|
||||
None => return warn!("{}: Discarding closed pipeline", pipeline_id),
|
||||
};
|
||||
browser.session_history.replace_reloader(
|
||||
webview.session_history.replace_reloader(
|
||||
NeedsToReload::No(pipeline_id),
|
||||
NeedsToReload::Yes(pipeline_id, load_data),
|
||||
);
|
||||
|
@ -5350,17 +5385,10 @@ where
|
|||
&mut self,
|
||||
top_level_id: TopLevelBrowsingContextId,
|
||||
) -> &mut JointSessionHistory {
|
||||
&mut self
|
||||
.browsers
|
||||
.entry(top_level_id)
|
||||
// This shouldn't be necessary since `get_joint_session_history` is
|
||||
// invoked for existing browsers but we need this to satisfy the
|
||||
// type system.
|
||||
.or_insert_with(|| Browser {
|
||||
focused_browsing_context_id: BrowsingContextId::from(top_level_id),
|
||||
session_history: JointSessionHistory::new(),
|
||||
})
|
||||
.session_history
|
||||
self.webviews
|
||||
.get_mut(top_level_id)
|
||||
.map(|webview| &mut webview.session_history)
|
||||
.expect("Unknown top-level browsing context")
|
||||
}
|
||||
|
||||
// Convert a browsing context to a sendable form to pass to the compositor
|
||||
|
@ -5392,29 +5420,23 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
/// Re-send the frame tree to the compositor.
|
||||
fn update_frame_tree_if_active(
|
||||
/// Send the frame tree for the given webview to the compositor.
|
||||
fn update_frame_tree_if_focused(
|
||||
&mut self,
|
||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
) {
|
||||
// Only send the frame tree if it's the active one or if no frame tree
|
||||
// has been sent yet.
|
||||
if self.active_browser_id.is_none() ||
|
||||
Some(top_level_browsing_context_id) == self.active_browser_id
|
||||
{
|
||||
self.send_frame_tree(top_level_browsing_context_id);
|
||||
// Only send the frame tree if the given webview is focused.
|
||||
if let Some(focused_webview_id) = self.webviews.focused_webview().map(|(id, _)| id) {
|
||||
if top_level_browsing_context_id != focused_webview_id {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send the current frame tree to compositor
|
||||
fn send_frame_tree(&mut self, top_level_browsing_context_id: TopLevelBrowsingContextId) {
|
||||
// Note that this function can panic, due to ipc-channel creation failure.
|
||||
// avoiding this panic would require a mechanism for dealing
|
||||
// with low-resource scenarios.
|
||||
let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id);
|
||||
if let Some(frame_tree) = self.browsing_context_to_sendable(browsing_context_id) {
|
||||
debug!("{}: Sending frame tree", browsing_context_id);
|
||||
self.active_browser_id = Some(top_level_browsing_context_id);
|
||||
self.compositor_proxy
|
||||
.send(CompositorMsg::SetFrameTree(frame_tree));
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ mod sandboxing;
|
|||
mod serviceworker;
|
||||
mod session_history;
|
||||
mod timer_scheduler;
|
||||
mod webview;
|
||||
|
||||
pub use crate::constellation::{Constellation, InitialConstellationState};
|
||||
pub use crate::logging::{FromCompositorLogger, FromScriptLogger};
|
||||
|
|
200
components/constellation/webview.rs
Normal file
200
components/constellation/webview.rs
Normal file
|
@ -0,0 +1,200 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use msg::constellation_msg::TopLevelBrowsingContextId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WebViewManager<WebView> {
|
||||
/// Our top-level browsing contexts. In the WebRender scene, their pipelines are the children of
|
||||
/// a single root pipeline that also applies any pinch zoom transformation.
|
||||
webviews: HashMap<TopLevelBrowsingContextId, WebView>,
|
||||
|
||||
/// The order in which they were focused, latest last.
|
||||
focus_order: Vec<TopLevelBrowsingContextId>,
|
||||
|
||||
/// Whether the latest webview in focus order is currently focused.
|
||||
is_focused: bool,
|
||||
}
|
||||
|
||||
impl<WebView> Default for WebViewManager<WebView> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
webviews: HashMap::default(),
|
||||
focus_order: Vec::default(),
|
||||
is_focused: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<WebView> WebViewManager<WebView> {
|
||||
pub fn add(
|
||||
&mut self,
|
||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
webview: WebView,
|
||||
) {
|
||||
self.webviews.insert(top_level_browsing_context_id, webview);
|
||||
}
|
||||
|
||||
pub fn remove(
|
||||
&mut self,
|
||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
) -> Option<WebView> {
|
||||
if self.focus_order.last() == Some(&top_level_browsing_context_id) {
|
||||
self.is_focused = false;
|
||||
}
|
||||
self.focus_order
|
||||
.retain(|b| *b != top_level_browsing_context_id);
|
||||
self.webviews.remove(&top_level_browsing_context_id)
|
||||
}
|
||||
|
||||
pub fn get(
|
||||
&self,
|
||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
) -> Option<&WebView> {
|
||||
self.webviews.get(&top_level_browsing_context_id)
|
||||
}
|
||||
|
||||
pub fn get_mut(
|
||||
&mut self,
|
||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
) -> Option<&mut WebView> {
|
||||
self.webviews.get_mut(&top_level_browsing_context_id)
|
||||
}
|
||||
|
||||
pub fn focused_webview(&self) -> Option<(TopLevelBrowsingContextId, &WebView)> {
|
||||
if !self.is_focused {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(top_level_browsing_context_id) = self.focus_order.last().cloned() {
|
||||
debug_assert!(
|
||||
self.webviews.contains_key(&top_level_browsing_context_id),
|
||||
"BUG: webview in .focus_order not in .webviews!",
|
||||
);
|
||||
self.get(top_level_browsing_context_id)
|
||||
.map(|webview| (top_level_browsing_context_id, webview))
|
||||
} else {
|
||||
debug_assert!(false, "BUG: .is_focused but no webviews in .focus_order!");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focus(&mut self, top_level_browsing_context_id: TopLevelBrowsingContextId) {
|
||||
debug_assert!(self.webviews.contains_key(&top_level_browsing_context_id));
|
||||
self.focus_order
|
||||
.retain(|b| *b != top_level_browsing_context_id);
|
||||
self.focus_order.push(top_level_browsing_context_id);
|
||||
self.is_focused = true;
|
||||
}
|
||||
|
||||
pub fn unfocus(&mut self) {
|
||||
self.is_focused = false;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use msg::constellation_msg::{
|
||||
BrowsingContextId, BrowsingContextIndex, PipelineNamespace, PipelineNamespaceId,
|
||||
TopLevelBrowsingContextId,
|
||||
};
|
||||
|
||||
use crate::webview::WebViewManager;
|
||||
|
||||
fn top_level_id(namespace_id: u32, index: u32) -> TopLevelBrowsingContextId {
|
||||
TopLevelBrowsingContextId(BrowsingContextId {
|
||||
namespace_id: PipelineNamespaceId(namespace_id),
|
||||
index: BrowsingContextIndex(NonZeroU32::new(index).expect("Incorrect test case")),
|
||||
})
|
||||
}
|
||||
|
||||
fn webviews_sorted<WebView: Clone>(
|
||||
webviews: &WebViewManager<WebView>,
|
||||
) -> Vec<(TopLevelBrowsingContextId, WebView)> {
|
||||
let mut keys = webviews.webviews.keys().collect::<Vec<_>>();
|
||||
keys.sort();
|
||||
keys.iter()
|
||||
.map(|&id| {
|
||||
(
|
||||
*id,
|
||||
webviews
|
||||
.webviews
|
||||
.get(id)
|
||||
.cloned()
|
||||
.expect("Incorrect test case"),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
PipelineNamespace::install(PipelineNamespaceId(0));
|
||||
let mut webviews = WebViewManager::default();
|
||||
|
||||
// add() adds the webview to the map, but does not focus it.
|
||||
webviews.add(TopLevelBrowsingContextId::new(), 'a');
|
||||
webviews.add(TopLevelBrowsingContextId::new(), 'b');
|
||||
webviews.add(TopLevelBrowsingContextId::new(), 'c');
|
||||
assert_eq!(
|
||||
webviews_sorted(&webviews),
|
||||
vec![
|
||||
(top_level_id(0, 1), 'a'),
|
||||
(top_level_id(0, 2), 'b'),
|
||||
(top_level_id(0, 3), 'c'),
|
||||
]
|
||||
);
|
||||
assert!(webviews.focus_order.is_empty());
|
||||
assert_eq!(webviews.is_focused, false);
|
||||
|
||||
// focus() makes the given webview the latest in focus order.
|
||||
webviews.focus(top_level_id(0, 2));
|
||||
assert_eq!(webviews.focus_order, vec![top_level_id(0, 2)]);
|
||||
assert_eq!(webviews.is_focused, true);
|
||||
webviews.focus(top_level_id(0, 1));
|
||||
assert_eq!(
|
||||
webviews.focus_order,
|
||||
vec![top_level_id(0, 2), top_level_id(0, 1)]
|
||||
);
|
||||
assert_eq!(webviews.is_focused, true);
|
||||
webviews.focus(top_level_id(0, 3));
|
||||
assert_eq!(
|
||||
webviews.focus_order,
|
||||
vec![top_level_id(0, 2), top_level_id(0, 1), top_level_id(0, 3)]
|
||||
);
|
||||
assert_eq!(webviews.is_focused, true);
|
||||
|
||||
// unfocus() clears the “is focused” flag, but does not touch the focus order.
|
||||
webviews.unfocus();
|
||||
assert_eq!(
|
||||
webviews.focus_order,
|
||||
vec![top_level_id(0, 2), top_level_id(0, 1), top_level_id(0, 3)]
|
||||
);
|
||||
assert_eq!(webviews.is_focused, false);
|
||||
|
||||
// focus() avoids duplicates in focus order, when the given webview has been focused before.
|
||||
webviews.focus(top_level_id(0, 1));
|
||||
assert_eq!(
|
||||
webviews.focus_order,
|
||||
vec![top_level_id(0, 2), top_level_id(0, 3), top_level_id(0, 1)]
|
||||
);
|
||||
assert_eq!(webviews.is_focused, true);
|
||||
|
||||
// remove() clears the “is focused” flag iff the given webview was focused.
|
||||
webviews.remove(top_level_id(0, 2));
|
||||
assert_eq!(webviews.is_focused, true);
|
||||
webviews.remove(top_level_id(0, 1));
|
||||
assert_eq!(webviews.is_focused, false);
|
||||
webviews.remove(top_level_id(0, 3));
|
||||
assert_eq!(webviews.is_focused, false);
|
||||
|
||||
// remove() removes the given webview from both the map and the focus order.
|
||||
assert!(webviews_sorted(&webviews).is_empty());
|
||||
assert!(webviews.focus_order.is_empty());
|
||||
}
|
||||
}
|
|
@ -790,13 +790,14 @@ impl WindowMethods for Window {
|
|||
let window = this.root();
|
||||
let document = window.Document();
|
||||
// https://html.spec.whatwg.org/multipage/#closing-browsing-contexts
|
||||
// Step 1, prompt to unload.
|
||||
// Step 1, check if traversable is closing, was already done above.
|
||||
// Steps 2 and 3, prompt to unload for all inclusive descendant navigables.
|
||||
// TODO: We should be prompting for all inclusive descendant navigables,
|
||||
// but we pass false here, which suggests we are not doing that. Why?
|
||||
if document.prompt_to_unload(false) {
|
||||
// Step 2, unload.
|
||||
// Step 4, unload.
|
||||
document.unload(false);
|
||||
// Step 3, remove from the user interface
|
||||
let _ = window.send_to_embedder(EmbedderMsg::CloseBrowser);
|
||||
// Step 4, discard browsing context.
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#a-browsing-context-is-discarded
|
||||
// which calls into https://html.spec.whatwg.org/multipage/#discard-a-document.
|
||||
window.discard_browsing_context();
|
||||
|
|
|
@ -293,7 +293,7 @@ impl WindowProxy {
|
|||
.and_then(|id| ScriptThread::find_document(id))
|
||||
.and_then(|doc| Some(DomRoot::from_ref(doc.window())))
|
||||
.unwrap();
|
||||
let msg = EmbedderMsg::AllowOpeningBrowser(chan);
|
||||
let msg = EmbedderMsg::AllowOpeningWebView(chan);
|
||||
window.send_to_embedder(msg);
|
||||
if port.recv().unwrap() {
|
||||
let new_top_level_browsing_context_id = TopLevelBrowsingContextId::new();
|
||||
|
@ -337,7 +337,7 @@ impl WindowProxy {
|
|||
let constellation_msg = ScriptMsg::ScriptNewAuxiliary(load_info, pipeline_sender);
|
||||
window.send_to_constellation(constellation_msg);
|
||||
ScriptThread::process_attach_layout(new_layout_info, document.origin().clone());
|
||||
let msg = EmbedderMsg::BrowserCreated(new_top_level_browsing_context_id);
|
||||
let msg = EmbedderMsg::WebViewOpened(new_top_level_browsing_context_id);
|
||||
window.send_to_embedder(msg);
|
||||
// TODO: if noopener is false, copy the sessionStorage storage area of the creator origin.
|
||||
// See step 14 of https://html.spec.whatwg.org/multipage/#creating-a-new-browsing-context
|
||||
|
|
|
@ -633,8 +633,8 @@ where
|
|||
self.compositor.capture_webrender();
|
||||
},
|
||||
|
||||
EmbedderEvent::NewBrowser(url, top_level_browsing_context_id) => {
|
||||
let msg = ConstellationMsg::NewBrowser(url, top_level_browsing_context_id);
|
||||
EmbedderEvent::NewWebView(url, top_level_browsing_context_id) => {
|
||||
let msg = ConstellationMsg::NewWebView(url, top_level_browsing_context_id);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!(
|
||||
"Sending NewBrowser message to constellation failed ({:?}).",
|
||||
|
@ -643,18 +643,18 @@ where
|
|||
}
|
||||
},
|
||||
|
||||
EmbedderEvent::SelectBrowser(top_level_browsing_context_id) => {
|
||||
let msg = ConstellationMsg::SelectBrowser(top_level_browsing_context_id);
|
||||
EmbedderEvent::FocusWebView(top_level_browsing_context_id) => {
|
||||
let msg = ConstellationMsg::FocusWebView(top_level_browsing_context_id);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!(
|
||||
"Sending SelectBrowser message to constellation failed ({:?}).",
|
||||
"Sending FocusBrowser message to constellation failed ({:?}).",
|
||||
e
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
EmbedderEvent::CloseBrowser(top_level_browsing_context_id) => {
|
||||
let msg = ConstellationMsg::CloseBrowser(top_level_browsing_context_id);
|
||||
EmbedderEvent::CloseWebView(top_level_browsing_context_id) => {
|
||||
let msg = ConstellationMsg::CloseWebView(top_level_browsing_context_id);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!(
|
||||
"Sending CloseBrowser message to constellation failed ({:?}).",
|
||||
|
@ -683,14 +683,11 @@ where
|
|||
}
|
||||
},
|
||||
|
||||
EmbedderEvent::ChangeBrowserVisibility(top_level_browsing_context_id, visible) => {
|
||||
let msg = ConstellationMsg::ChangeBrowserVisibility(
|
||||
top_level_browsing_context_id,
|
||||
visible,
|
||||
);
|
||||
EmbedderEvent::WebViewVisibilityChanged(webview_id, visible) => {
|
||||
let msg = ConstellationMsg::WebViewVisibilityChanged(webview_id, visible);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!(
|
||||
"Sending ChangeBrowserVisibility to constellation failed ({:?}).",
|
||||
"Sending WebViewVisibilityChanged to constellation failed ({:?}).",
|
||||
e
|
||||
);
|
||||
}
|
||||
|
|
|
@ -55,13 +55,15 @@ pub enum ConstellationMsg {
|
|||
/// A log entry, with the top-level browsing context id and thread name
|
||||
LogEntry(Option<TopLevelBrowsingContextId>, Option<String>, LogEntry),
|
||||
/// Create a new top level browsing context.
|
||||
NewBrowser(ServoUrl, TopLevelBrowsingContextId),
|
||||
NewWebView(ServoUrl, TopLevelBrowsingContextId),
|
||||
/// Close a top level browsing context.
|
||||
CloseBrowser(TopLevelBrowsingContextId),
|
||||
CloseWebView(TopLevelBrowsingContextId),
|
||||
/// Panic a top level browsing context.
|
||||
SendError(Option<TopLevelBrowsingContextId>, String),
|
||||
/// Make browser visible.
|
||||
SelectBrowser(TopLevelBrowsingContextId),
|
||||
/// Make a top-level browsing context focused.
|
||||
FocusWebView(TopLevelBrowsingContextId),
|
||||
/// Make none of the top-level browsing contexts focused.
|
||||
BlurWebView,
|
||||
/// Forward an event to the script task of the given pipeline.
|
||||
ForwardEvent(PipelineId, CompositorEvent),
|
||||
/// Requesting a change to the onscreen cursor.
|
||||
|
@ -74,8 +76,8 @@ pub enum ConstellationMsg {
|
|||
ExitFullScreen(TopLevelBrowsingContextId),
|
||||
/// Media session action.
|
||||
MediaSessionAction(MediaSessionActionType),
|
||||
/// Toggle browser visibility.
|
||||
ChangeBrowserVisibility(TopLevelBrowsingContextId, bool),
|
||||
/// The visibility of the webview has changed.
|
||||
WebViewVisibilityChanged(TopLevelBrowsingContextId, bool),
|
||||
/// Virtual keyboard was dismissed
|
||||
IMEDismissed,
|
||||
/// Compositing done, but external code needs to present.
|
||||
|
@ -100,17 +102,18 @@ impl fmt::Debug for ConstellationMsg {
|
|||
WebDriverCommand(..) => "WebDriverCommand",
|
||||
Reload(..) => "Reload",
|
||||
LogEntry(..) => "LogEntry",
|
||||
NewBrowser(..) => "NewBrowser",
|
||||
CloseBrowser(..) => "CloseBrowser",
|
||||
NewWebView(..) => "NewWebView",
|
||||
CloseWebView(..) => "CloseWebView",
|
||||
FocusWebView(..) => "FocusWebView",
|
||||
BlurWebView => "BlurWebView",
|
||||
SendError(..) => "SendError",
|
||||
SelectBrowser(..) => "SelectBrowser",
|
||||
ForwardEvent(..) => "ForwardEvent",
|
||||
SetCursor(..) => "SetCursor",
|
||||
EnableProfiler(..) => "EnableProfiler",
|
||||
DisableProfiler => "DisableProfiler",
|
||||
ExitFullScreen(..) => "ExitFullScreen",
|
||||
MediaSessionAction(..) => "MediaSessionAction",
|
||||
ChangeBrowserVisibility(..) => "ChangeBrowserVisibility",
|
||||
WebViewVisibilityChanged(..) => "WebViewVisibilityChanged",
|
||||
IMEDismissed => "IMEDismissed",
|
||||
ClearCache => "ClearCache",
|
||||
ReadyToPresent(..) => "ReadyToPresent",
|
||||
|
|
|
@ -155,9 +155,15 @@ pub enum EmbedderMsg {
|
|||
/// Whether or not to allow a pipeline to load a url.
|
||||
AllowNavigationRequest(PipelineId, ServoUrl),
|
||||
/// Whether or not to allow script to open a new tab/browser
|
||||
AllowOpeningBrowser(IpcSender<bool>),
|
||||
/// A new browser was created by script
|
||||
BrowserCreated(TopLevelBrowsingContextId),
|
||||
AllowOpeningWebView(IpcSender<bool>),
|
||||
/// A browser was created
|
||||
WebViewOpened(TopLevelBrowsingContextId),
|
||||
/// A browser was destroyed
|
||||
WebViewClosed(TopLevelBrowsingContextId),
|
||||
/// A browser gained focus for keyboard events
|
||||
WebViewFocused(TopLevelBrowsingContextId),
|
||||
/// All browsers lost focus for keyboard events
|
||||
WebViewBlurred,
|
||||
/// Wether or not to unload a document
|
||||
AllowUnload(IpcSender<bool>),
|
||||
/// Sends an unconsumed key event back to the embedder.
|
||||
|
@ -180,8 +186,6 @@ pub enum EmbedderMsg {
|
|||
LoadStart,
|
||||
/// The load of a page has completed
|
||||
LoadComplete,
|
||||
/// A browser is to be closed
|
||||
CloseBrowser,
|
||||
/// A pipeline panicked. First string is the reason, second one is the backtrace.
|
||||
Panic(String, Option<String>),
|
||||
/// Open dialog to select bluetooth device.
|
||||
|
@ -241,7 +245,6 @@ impl Debug for EmbedderMsg {
|
|||
EmbedderMsg::SetCursor(..) => write!(f, "SetCursor"),
|
||||
EmbedderMsg::NewFavicon(..) => write!(f, "NewFavicon"),
|
||||
EmbedderMsg::HeadParsed => write!(f, "HeadParsed"),
|
||||
EmbedderMsg::CloseBrowser => write!(f, "CloseBrowser"),
|
||||
EmbedderMsg::HistoryChanged(..) => write!(f, "HistoryChanged"),
|
||||
EmbedderMsg::SetFullscreenState(..) => write!(f, "SetFullscreenState"),
|
||||
EmbedderMsg::LoadStart => write!(f, "LoadStart"),
|
||||
|
@ -253,8 +256,11 @@ impl Debug for EmbedderMsg {
|
|||
EmbedderMsg::ShowIME(..) => write!(f, "ShowIME"),
|
||||
EmbedderMsg::HideIME => write!(f, "HideIME"),
|
||||
EmbedderMsg::Shutdown => write!(f, "Shutdown"),
|
||||
EmbedderMsg::AllowOpeningBrowser(..) => write!(f, "AllowOpeningBrowser"),
|
||||
EmbedderMsg::BrowserCreated(..) => write!(f, "BrowserCreated"),
|
||||
EmbedderMsg::AllowOpeningWebView(..) => write!(f, "AllowOpeningWebView"),
|
||||
EmbedderMsg::WebViewOpened(..) => write!(f, "WebViewOpened"),
|
||||
EmbedderMsg::WebViewClosed(..) => write!(f, "WebViewClosed"),
|
||||
EmbedderMsg::WebViewFocused(..) => write!(f, "WebViewFocused"),
|
||||
EmbedderMsg::WebViewBlurred => write!(f, "WebViewUnfocused"),
|
||||
EmbedderMsg::ReportProfile(..) => write!(f, "ReportProfile"),
|
||||
EmbedderMsg::MediaSessionEvent(..) => write!(f, "MediaSessionEvent"),
|
||||
EmbedderMsg::OnDevtoolsStarted(..) => write!(f, "OnDevtoolsStarted"),
|
||||
|
|
|
@ -257,8 +257,13 @@ impl BrowsingContextId {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Eq, Hash, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub struct BrowsingContextGroupId(pub u32);
|
||||
impl fmt::Display for BrowsingContextGroupId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "BrowsingContextGroup{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
thread_local!(pub static TOP_LEVEL_BROWSING_CONTEXT_ID: Cell<Option<TopLevelBrowsingContextId>> = Cell::new(None));
|
||||
|
||||
|
@ -266,6 +271,7 @@ thread_local!(pub static TOP_LEVEL_BROWSING_CONTEXT_ID: Cell<Option<TopLevelBrow
|
|||
Clone, Copy, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
pub struct TopLevelBrowsingContextId(pub BrowsingContextId);
|
||||
pub type WebViewId = TopLevelBrowsingContextId;
|
||||
|
||||
size_of_test!(TopLevelBrowsingContextId, 8);
|
||||
size_of_test!(Option<TopLevelBrowsingContextId>, 8);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue