mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Make auxiliary webviews exist in the constellation immediately (#35672)
To allow embedders to interact with webviews as soon as they are created, we need to ensure that they exist in both the compositor and the constellation before those interactions happen. #35662 does this for the compositor, while this patch does this for the constellation. When a webview opens another webview (via <a target>, <form target>, window.open(), etc), the embedder creates an “auxiliary” webview, which previously went as follows: - script create_auxiliary_browsing_context - libservo AllowOpeningWebView - embedder request_open_auxiliary_webview (→ constellation FocusWebView) - script create_auxiliary_browsing_context - constellation ScriptNewAuxiliary In that model, the constellation may receive FocusWebView before it receives ScriptNewAuxiliary. Now they are created as follows: - script create_auxiliary_browsing_context - constellation CreateAuxiliaryWebView - libservo AllowOpeningWebView - embedder request_open_auxiliary_webview (→ constellation FocusWebView) - constellation CreateAuxiliaryWebView - script create_auxiliary_browsing_context Since these messages are all synchronous and the constellation will have set up the webview before handling any new messages, the webview will always exist by the time we handle the embedder’s FocusWebView. Signed-off-by: Delan Azabani <dazabani@igalia.com> Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
3d459badc0
commit
b4e2037dbe
5 changed files with 108 additions and 86 deletions
|
@ -138,14 +138,14 @@ use net_traits::{self, IpcSend, ReferrerPolicy, ResourceThreads};
|
|||
use profile_traits::{mem, time};
|
||||
use script_layout_interface::{LayoutFactory, ScriptThreadFactory};
|
||||
use script_traits::{
|
||||
AnimationState, AnimationTickType, AuxiliaryBrowsingContextLoadInfo, BroadcastMsg,
|
||||
ConstellationInputEvent, DiscardBrowsingContext, DocumentActivity, DocumentState,
|
||||
IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job,
|
||||
LayoutMsg as FromLayoutMsg, LoadData, LoadOrigin, LogEntry, MessagePortMsg,
|
||||
NavigationHistoryBehavior, PortMessageTask, SWManagerMsg, SWManagerSenders,
|
||||
ScriptMsg as FromScriptMsg, ScriptThreadMessage, ScriptToConstellationChan,
|
||||
ServiceWorkerManagerFactory, ServiceWorkerMsg, StructuredSerializedData,
|
||||
UpdatePipelineIdReason, WindowSizeData, WindowSizeType,
|
||||
AnimationState, AnimationTickType, AuxiliaryWebViewCreationRequest,
|
||||
AuxiliaryWebViewCreationResponse, BroadcastMsg, ConstellationInputEvent,
|
||||
DiscardBrowsingContext, DocumentActivity, DocumentState, IFrameLoadInfo,
|
||||
IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job, LayoutMsg as FromLayoutMsg,
|
||||
LoadData, LoadOrigin, LogEntry, MessagePortMsg, NavigationHistoryBehavior, PortMessageTask,
|
||||
SWManagerMsg, SWManagerSenders, ScriptMsg as FromScriptMsg, ScriptThreadMessage,
|
||||
ScriptToConstellationChan, ServiceWorkerManagerFactory, ServiceWorkerMsg,
|
||||
StructuredSerializedData, UpdatePipelineIdReason, WindowSizeData, WindowSizeType,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use servo_config::{opts, pref};
|
||||
|
@ -1538,7 +1538,7 @@ where
|
|||
FromScriptMsg::ScriptNewIFrame(load_info) => {
|
||||
self.handle_script_new_iframe(load_info);
|
||||
},
|
||||
FromScriptMsg::ScriptNewAuxiliary(load_info) => {
|
||||
FromScriptMsg::CreateAuxiliaryWebView(load_info) => {
|
||||
self.handle_script_new_auxiliary(load_info);
|
||||
},
|
||||
FromScriptMsg::ChangeRunningAnimationsState(animation_state) => {
|
||||
|
@ -3324,15 +3324,35 @@ where
|
|||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
)]
|
||||
fn handle_script_new_auxiliary(&mut self, load_info: AuxiliaryBrowsingContextLoadInfo) {
|
||||
let AuxiliaryBrowsingContextLoadInfo {
|
||||
fn handle_script_new_auxiliary(&mut self, load_info: AuxiliaryWebViewCreationRequest) {
|
||||
let AuxiliaryWebViewCreationRequest {
|
||||
load_data,
|
||||
opener_webview_id,
|
||||
opener_pipeline_id,
|
||||
new_top_level_browsing_context_id,
|
||||
new_browsing_context_id,
|
||||
new_pipeline_id,
|
||||
response_sender,
|
||||
} = load_info;
|
||||
|
||||
let (webview_id_sender, webview_id_receiver) = match ipc::channel() {
|
||||
Ok(result) => result,
|
||||
Err(error) => {
|
||||
warn!("Failed to create channel: {error:?}");
|
||||
let _ = response_sender.send(None);
|
||||
return;
|
||||
},
|
||||
};
|
||||
self.embedder_proxy.send(EmbedderMsg::AllowOpeningWebView(
|
||||
opener_webview_id,
|
||||
webview_id_sender,
|
||||
));
|
||||
let new_webview_id = match webview_id_receiver.recv() {
|
||||
Ok(Some(webview_id)) => webview_id,
|
||||
Ok(None) | Err(_) => {
|
||||
let _ = response_sender.send(None);
|
||||
return;
|
||||
},
|
||||
};
|
||||
let new_browsing_context_id = BrowsingContextId::from(new_webview_id);
|
||||
|
||||
let (script_sender, opener_browsing_context_id) =
|
||||
match self.pipelines.get(&opener_pipeline_id) {
|
||||
Some(pipeline) => (pipeline.event_loop.clone(), pipeline.browsing_context_id),
|
||||
|
@ -3353,21 +3373,26 @@ where
|
|||
);
|
||||
},
|
||||
};
|
||||
let new_pipeline_id = PipelineId::new();
|
||||
let pipeline = Pipeline::new(
|
||||
new_pipeline_id,
|
||||
new_browsing_context_id,
|
||||
new_top_level_browsing_context_id,
|
||||
new_webview_id,
|
||||
Some(opener_browsing_context_id),
|
||||
script_sender,
|
||||
self.compositor_proxy.clone(),
|
||||
is_opener_throttled,
|
||||
load_data,
|
||||
);
|
||||
let _ = response_sender.send(Some(AuxiliaryWebViewCreationResponse {
|
||||
new_webview_id,
|
||||
new_pipeline_id,
|
||||
}));
|
||||
|
||||
assert!(!self.pipelines.contains_key(&new_pipeline_id));
|
||||
self.pipelines.insert(new_pipeline_id, pipeline);
|
||||
self.webviews.add(
|
||||
new_top_level_browsing_context_id,
|
||||
new_webview_id,
|
||||
WebView {
|
||||
focused_browsing_context_id: new_browsing_context_id,
|
||||
session_history: JointSessionHistory::new(),
|
||||
|
@ -3391,10 +3416,10 @@ where
|
|||
};
|
||||
bc_group
|
||||
.top_level_browsing_context_set
|
||||
.insert(new_top_level_browsing_context_id);
|
||||
.insert(new_webview_id);
|
||||
|
||||
self.add_pending_change(SessionHistoryChange {
|
||||
top_level_browsing_context_id: new_top_level_browsing_context_id,
|
||||
top_level_browsing_context_id: new_webview_id,
|
||||
browsing_context_id: new_browsing_context_id,
|
||||
new_pipeline_id,
|
||||
replace: None,
|
||||
|
|
|
@ -167,7 +167,7 @@ mod from_script {
|
|||
Self::SetThrottledComplete(..) => target!("SetThrottledComplete"),
|
||||
Self::ScriptLoadedURLInIFrame(..) => target!("ScriptLoadedURLInIFrame"),
|
||||
Self::ScriptNewIFrame(..) => target!("ScriptNewIFrame"),
|
||||
Self::ScriptNewAuxiliary(..) => target!("ScriptNewAuxiliary"),
|
||||
Self::CreateAuxiliaryWebView(..) => target!("ScriptNewAuxiliary"),
|
||||
Self::ActivateDocument => target!("ActivateDocument"),
|
||||
Self::SetDocumentState(..) => target!("SetDocumentState"),
|
||||
Self::SetLayoutEpoch(..) => target!("SetLayoutEpoch"),
|
||||
|
|
|
@ -7,7 +7,6 @@ use std::ptr;
|
|||
|
||||
use base::id::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId};
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::EmbedderMsg;
|
||||
use html5ever::local_name;
|
||||
use indexmap::map::IndexMap;
|
||||
use ipc_channel::ipc;
|
||||
|
@ -31,7 +30,7 @@ use js::JSCLASS_IS_GLOBAL;
|
|||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use net_traits::request::Referrer;
|
||||
use script_traits::{
|
||||
AuxiliaryBrowsingContextLoadInfo, LoadData, LoadOrigin, NavigationHistoryBehavior,
|
||||
AuxiliaryWebViewCreationRequest, LoadData, LoadOrigin, NavigationHistoryBehavior,
|
||||
NewLayoutInfo, ScriptMsg,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -285,70 +284,61 @@ impl WindowProxy {
|
|||
name: DOMString,
|
||||
noopener: bool,
|
||||
) -> Option<DomRoot<WindowProxy>> {
|
||||
let (chan, port) = ipc::channel().unwrap();
|
||||
let (response_sender, response_receiver) = ipc::channel().unwrap();
|
||||
let window = self
|
||||
.currently_active
|
||||
.get()
|
||||
.and_then(ScriptThread::find_document)
|
||||
.map(|doc| DomRoot::from_ref(doc.window()))
|
||||
.unwrap();
|
||||
let msg = EmbedderMsg::AllowOpeningWebView(window.webview_id(), chan);
|
||||
window.send_to_embedder(msg);
|
||||
if let Some(new_top_level_browsing_context_id) = port.recv().unwrap() {
|
||||
let new_browsing_context_id =
|
||||
BrowsingContextId::from(new_top_level_browsing_context_id);
|
||||
let new_pipeline_id = PipelineId::new();
|
||||
let document = self
|
||||
.currently_active
|
||||
.get()
|
||||
.and_then(ScriptThread::find_document)
|
||||
.expect("A WindowProxy creating an auxiliary to have an active document");
|
||||
|
||||
let blank_url = ServoUrl::parse("about:blank").ok().unwrap();
|
||||
let load_data = LoadData::new(
|
||||
LoadOrigin::Script(document.origin().immutable().clone()),
|
||||
blank_url,
|
||||
None,
|
||||
document.global().get_referrer(),
|
||||
document.get_referrer_policy(),
|
||||
None, // Doesn't inherit secure context
|
||||
None,
|
||||
);
|
||||
let load_info = AuxiliaryBrowsingContextLoadInfo {
|
||||
load_data: load_data.clone(),
|
||||
opener_pipeline_id: self.currently_active.get().unwrap(),
|
||||
new_browsing_context_id,
|
||||
new_top_level_browsing_context_id,
|
||||
new_pipeline_id,
|
||||
};
|
||||
let document = self
|
||||
.currently_active
|
||||
.get()
|
||||
.and_then(ScriptThread::find_document)
|
||||
.expect("A WindowProxy creating an auxiliary to have an active document");
|
||||
let blank_url = ServoUrl::parse("about:blank").ok().unwrap();
|
||||
let load_data = LoadData::new(
|
||||
LoadOrigin::Script(document.origin().immutable().clone()),
|
||||
blank_url,
|
||||
None,
|
||||
document.global().get_referrer(),
|
||||
document.get_referrer_policy(),
|
||||
None, // Doesn't inherit secure context
|
||||
None,
|
||||
);
|
||||
let load_info = AuxiliaryWebViewCreationRequest {
|
||||
load_data: load_data.clone(),
|
||||
opener_webview_id: window.webview_id(),
|
||||
opener_pipeline_id: self.currently_active.get().unwrap(),
|
||||
response_sender,
|
||||
};
|
||||
let constellation_msg = ScriptMsg::CreateAuxiliaryWebView(load_info);
|
||||
window.send_to_constellation(constellation_msg);
|
||||
|
||||
let new_layout_info = NewLayoutInfo {
|
||||
parent_info: None,
|
||||
new_pipeline_id,
|
||||
browsing_context_id: new_browsing_context_id,
|
||||
top_level_browsing_context_id: new_top_level_browsing_context_id,
|
||||
opener: Some(self.browsing_context_id),
|
||||
load_data,
|
||||
window_size: window.window_size(),
|
||||
};
|
||||
let constellation_msg = ScriptMsg::ScriptNewAuxiliary(load_info);
|
||||
window.send_to_constellation(constellation_msg);
|
||||
ScriptThread::process_attach_layout(new_layout_info, document.origin().clone());
|
||||
// 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
|
||||
let auxiliary =
|
||||
ScriptThread::find_document(new_pipeline_id).and_then(|doc| doc.browsing_context());
|
||||
if let Some(proxy) = auxiliary {
|
||||
if name.to_lowercase() != "_blank" {
|
||||
proxy.set_name(name);
|
||||
}
|
||||
if noopener {
|
||||
proxy.disown();
|
||||
}
|
||||
return Some(proxy);
|
||||
}
|
||||
let response = response_receiver.recv().unwrap()?;
|
||||
let new_browsing_context_id = BrowsingContextId::from(response.new_webview_id);
|
||||
let new_layout_info = NewLayoutInfo {
|
||||
parent_info: None,
|
||||
new_pipeline_id: response.new_pipeline_id,
|
||||
browsing_context_id: new_browsing_context_id,
|
||||
top_level_browsing_context_id: response.new_webview_id,
|
||||
opener: Some(self.browsing_context_id),
|
||||
load_data,
|
||||
window_size: window.window_size(),
|
||||
};
|
||||
ScriptThread::process_attach_layout(new_layout_info, document.origin().clone());
|
||||
// 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
|
||||
let new_window_proxy = ScriptThread::find_document(response.new_pipeline_id)
|
||||
.and_then(|doc| doc.browsing_context())?;
|
||||
if name.to_lowercase() != "_blank" {
|
||||
new_window_proxy.set_name(name);
|
||||
}
|
||||
None
|
||||
if noopener {
|
||||
new_window_proxy.disown();
|
||||
}
|
||||
Some(new_window_proxy)
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#delaying-load-events-mode>
|
||||
|
|
|
@ -22,7 +22,7 @@ use background_hang_monitor_api::BackgroundHangMonitorRegister;
|
|||
use base::cross_process_instant::CrossProcessInstant;
|
||||
use base::id::{
|
||||
BlobId, BrowsingContextId, HistoryStateId, MessagePortId, PipelineId, PipelineNamespaceId,
|
||||
TopLevelBrowsingContextId,
|
||||
TopLevelBrowsingContextId, WebViewId,
|
||||
};
|
||||
use base::Epoch;
|
||||
use bitflags::bitflags;
|
||||
|
@ -561,16 +561,23 @@ pub enum IFrameSandboxState {
|
|||
|
||||
/// Specifies the information required to load an auxiliary browsing context.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct AuxiliaryBrowsingContextLoadInfo {
|
||||
pub struct AuxiliaryWebViewCreationRequest {
|
||||
/// Load data containing the url to load
|
||||
pub load_data: LoadData,
|
||||
/// The webview that caused this request.
|
||||
pub opener_webview_id: WebViewId,
|
||||
/// The pipeline opener browsing context.
|
||||
pub opener_pipeline_id: PipelineId,
|
||||
/// The new top-level ID for the auxiliary.
|
||||
pub new_top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
/// The new browsing context ID.
|
||||
pub new_browsing_context_id: BrowsingContextId,
|
||||
/// The new pipeline ID for the auxiliary.
|
||||
/// Sender for the constellation’s response to our request.
|
||||
pub response_sender: IpcSender<Option<AuxiliaryWebViewCreationResponse>>,
|
||||
}
|
||||
|
||||
/// Constellation’s response to auxiliary browsing context creation requests.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct AuxiliaryWebViewCreationResponse {
|
||||
/// The new webview ID.
|
||||
pub new_webview_id: WebViewId,
|
||||
/// The new pipeline ID.
|
||||
pub new_pipeline_id: PipelineId,
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ use style_traits::CSSPixel;
|
|||
use webgpu::{wgc, WebGPU, WebGPUResponse};
|
||||
|
||||
use crate::{
|
||||
AnimationState, AuxiliaryBrowsingContextLoadInfo, BroadcastMsg, DocumentState,
|
||||
AnimationState, AuxiliaryWebViewCreationRequest, BroadcastMsg, DocumentState,
|
||||
IFrameLoadInfoWithData, LoadData, MessagePortMsg, NavigationHistoryBehavior, PortMessageTask,
|
||||
StructuredSerializedData, WindowSizeType, WorkerGlobalScopeInit, WorkerScriptLoadOrigin,
|
||||
};
|
||||
|
@ -207,7 +207,7 @@ pub enum ScriptMsg {
|
|||
/// A load of the initial `about:blank` has been completed in an IFrame.
|
||||
ScriptNewIFrame(IFrameLoadInfoWithData),
|
||||
/// Script has opened a new auxiliary browsing context.
|
||||
ScriptNewAuxiliary(AuxiliaryBrowsingContextLoadInfo),
|
||||
CreateAuxiliaryWebView(AuxiliaryWebViewCreationRequest),
|
||||
/// Mark a new document as active
|
||||
ActivateDocument,
|
||||
/// Set the document state for a pipeline (used by screenshot / reftests)
|
||||
|
@ -289,7 +289,7 @@ impl fmt::Debug for ScriptMsg {
|
|||
SetThrottledComplete(..) => "SetThrottledComplete",
|
||||
ScriptLoadedURLInIFrame(..) => "ScriptLoadedURLInIFrame",
|
||||
ScriptNewIFrame(..) => "ScriptNewIFrame",
|
||||
ScriptNewAuxiliary(..) => "ScriptNewAuxiliary",
|
||||
CreateAuxiliaryWebView(..) => "ScriptNewAuxiliary",
|
||||
ActivateDocument => "ActivateDocument",
|
||||
SetDocumentState(..) => "SetDocumentState",
|
||||
SetLayoutEpoch(..) => "SetLayoutEpoch",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue