mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
implement window.open, create auxiliary browsing context
This commit is contained in:
parent
3e96a322ae
commit
f408b798c4
93 changed files with 476 additions and 324 deletions
|
@ -123,7 +123,7 @@ use network_listener::NetworkListener;
|
|||
use pipeline::{InitialPipelineState, Pipeline};
|
||||
use profile_traits::mem;
|
||||
use profile_traits::time;
|
||||
use script_traits::{AnimationState, AnimationTickType, CompositorEvent};
|
||||
use script_traits::{AnimationState, AuxiliaryBrowsingContextLoadInfo, AnimationTickType, CompositorEvent};
|
||||
use script_traits::{ConstellationControlMsg, ConstellationMsg as FromCompositorMsg, DiscardBrowsingContext};
|
||||
use script_traits::{DocumentActivity, DocumentState, LayoutControlMsg, LoadData};
|
||||
use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg};
|
||||
|
@ -1121,6 +1121,9 @@ where
|
|||
FromScriptMsg::ScriptNewIFrame(load_info, layout_sender) => {
|
||||
self.handle_script_new_iframe(load_info, layout_sender);
|
||||
},
|
||||
FromScriptMsg::ScriptNewAuxiliary(load_info, layout_sender) => {
|
||||
self.handle_script_new_auxiliary(load_info, layout_sender);
|
||||
},
|
||||
FromScriptMsg::ChangeRunningAnimationsState(animation_state) => {
|
||||
self.handle_change_running_animations_state(source_pipeline_id, animation_state)
|
||||
},
|
||||
|
@ -1868,6 +1871,62 @@ where
|
|||
});
|
||||
}
|
||||
|
||||
fn handle_script_new_auxiliary(&mut self,
|
||||
load_info: AuxiliaryBrowsingContextLoadInfo,
|
||||
layout_sender: IpcSender<LayoutControlMsg>) {
|
||||
let AuxiliaryBrowsingContextLoadInfo {
|
||||
opener_pipeline_id,
|
||||
new_top_level_browsing_context_id,
|
||||
new_browsing_context_id,
|
||||
new_pipeline_id,
|
||||
} = load_info;
|
||||
|
||||
let url = ServoUrl::parse("about:blank").expect("infallible");
|
||||
|
||||
// TODO: Referrer?
|
||||
let load_data = LoadData::new(url.clone(), None, None, None);
|
||||
|
||||
let pipeline = {
|
||||
let opener_pipeline = match self.pipelines.get(&opener_pipeline_id) {
|
||||
Some(parent_pipeline) => parent_pipeline,
|
||||
None => return warn!("Auxiliary loaded url in closed pipeline {}.", opener_pipeline_id),
|
||||
};
|
||||
let opener_host = match reg_host(&opener_pipeline.url) {
|
||||
Some(host) => host,
|
||||
None => return warn!("Auxiliary loaded pipeline with no url {}.", opener_pipeline_id),
|
||||
};
|
||||
let script_sender = opener_pipeline.event_loop.clone();
|
||||
// https://html.spec.whatwg.org/multipage/#unit-of-related-similar-origin-browsing-contexts
|
||||
// If the auxiliary shares the host/scheme with the creator, they share an event-loop.
|
||||
// So the first entry for the auxiliary, itself currently "about:blank",
|
||||
// is the event-loop for the current host of the creator.
|
||||
self.event_loops.entry(new_top_level_browsing_context_id)
|
||||
.or_insert_with(HashMap::new)
|
||||
.insert(opener_host, Rc::downgrade(&script_sender));
|
||||
Pipeline::new(new_pipeline_id,
|
||||
new_browsing_context_id,
|
||||
new_top_level_browsing_context_id,
|
||||
None,
|
||||
script_sender,
|
||||
layout_sender,
|
||||
self.compositor_proxy.clone(),
|
||||
opener_pipeline.is_private,
|
||||
url,
|
||||
opener_pipeline.visible,
|
||||
load_data)
|
||||
};
|
||||
|
||||
assert!(!self.pipelines.contains_key(&new_pipeline_id));
|
||||
self.pipelines.insert(new_pipeline_id, pipeline);
|
||||
self.joint_session_histories.insert(new_top_level_browsing_context_id, JointSessionHistory::new());
|
||||
self.add_pending_change(SessionHistoryChange {
|
||||
top_level_browsing_context_id: new_top_level_browsing_context_id,
|
||||
browsing_context_id: new_browsing_context_id,
|
||||
new_pipeline_id: new_pipeline_id,
|
||||
replace: None,
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_pending_paint_metric(&self, pipeline_id: PipelineId, epoch: Epoch) {
|
||||
self.compositor_proxy
|
||||
.send(ToCompositorMsg::PendingPaintMetric(pipeline_id, epoch))
|
||||
|
|
|
@ -84,6 +84,10 @@ pub enum EmbedderMsg {
|
|||
Alert(String, IpcSender<()>),
|
||||
/// Wether or not to follow a link
|
||||
AllowNavigation(ServoUrl, IpcSender<bool>),
|
||||
/// Whether or not to allow script to open a new tab/browser
|
||||
AllowOpeningBrowser(IpcSender<bool>),
|
||||
/// A new browser was created by script
|
||||
BrowserCreated(TopLevelBrowsingContextId),
|
||||
/// Wether or not to unload a document
|
||||
AllowUnload(IpcSender<bool>),
|
||||
/// Sends an unconsumed key event back to the embedder.
|
||||
|
@ -143,6 +147,8 @@ 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -574,45 +574,61 @@ impl Activatable for HTMLAnchorElement {
|
|||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name>
|
||||
fn is_current_browsing_context(target: DOMString) -> bool {
|
||||
target.is_empty() || target == "_self"
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#following-hyperlinks-2>
|
||||
pub fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>, referrer_policy: Option<ReferrerPolicy>) {
|
||||
// Step 1: replace.
|
||||
// Step 2: source browsing context.
|
||||
// Step 3: target browsing context.
|
||||
let target = subject.get_attribute(&ns!(), &local_name!("target"));
|
||||
// Step 1: TODO: If subject cannot navigate, then return.
|
||||
// Step 2, done in Step 7.
|
||||
|
||||
// Step 4: disown target's opener if needed.
|
||||
let attribute = subject.get_attribute(&ns!(), &local_name!("href")).unwrap();
|
||||
let mut href = attribute.Value();
|
||||
|
||||
// Step 7: append a hyperlink suffix.
|
||||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=28925
|
||||
if let Some(suffix) = hyperlink_suffix {
|
||||
href.push_str(&suffix);
|
||||
}
|
||||
|
||||
// Step 5: parse the URL.
|
||||
// Step 6: navigate to an error document if parsing failed.
|
||||
let document = document_from_node(subject);
|
||||
let url = match document.url().join(&href) {
|
||||
Ok(url) => url,
|
||||
Err(_) => return,
|
||||
let window = document.window();
|
||||
|
||||
// Step 3: source browsing context.
|
||||
let source = document.browsing_context().unwrap();
|
||||
|
||||
// Step 4-5: target attribute.
|
||||
let target_attribute_value = subject.get_attribute(&ns!(), &local_name!("target"));
|
||||
|
||||
// Step 6.
|
||||
let noopener = if let Some(link_types) = subject.get_attribute(&ns!(), &local_name!("rel")) {
|
||||
let values = link_types.Value();
|
||||
let contains_noopener = values.contains("noopener");
|
||||
let contains_noreferrer = values.contains("noreferrer");
|
||||
contains_noreferrer || contains_noopener
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// Step 8: navigate to the URL.
|
||||
if let Some(target) = target {
|
||||
if !is_current_browsing_context(target.Value()) {
|
||||
// https://github.com/servo/servo/issues/13241
|
||||
// Step 7.
|
||||
let (maybe_chosen, replace) = match target_attribute_value {
|
||||
Some(name) => source.choose_browsing_context(name.Value(), noopener),
|
||||
None => (Some(window.window_proxy()), false)
|
||||
};
|
||||
let chosen = match maybe_chosen {
|
||||
Some(proxy) => proxy,
|
||||
None => return,
|
||||
};
|
||||
if let Some(target_document) = chosen.document() {
|
||||
let target_window = target_document.window();
|
||||
|
||||
// Step 9, dis-owning target's opener, if necessary
|
||||
// will have been done as part of Step 7 above
|
||||
// in choose_browsing_context/create_auxiliary_browsing_context.
|
||||
|
||||
// Step 10, 11, 12, 13. TODO: if parsing the URL failed, navigate to error page.
|
||||
let attribute = subject.get_attribute(&ns!(), &local_name!("href")).unwrap();
|
||||
let mut href = attribute.Value();
|
||||
// Step 12: append a hyperlink suffix.
|
||||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=28925
|
||||
if let Some(suffix) = hyperlink_suffix {
|
||||
href.push_str(&suffix);
|
||||
}
|
||||
}
|
||||
let url = match document.url().join(&href) {
|
||||
Ok(url) => url,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
debug!("following hyperlink to {}", url);
|
||||
|
||||
let window = document.window();
|
||||
window.load_url(url, false, false, referrer_policy);
|
||||
// Step 13, 14.
|
||||
debug!("following hyperlink to {}", url);
|
||||
target_window.load_url(url, replace, false, referrer_policy);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ use dom::node::{Node, NodeFlags, UnbindContext, VecPreOrderInsertionHelper};
|
|||
use dom::node::{document_from_node, window_from_node};
|
||||
use dom::validitystate::ValidationFlags;
|
||||
use dom::virtualmethods::VirtualMethods;
|
||||
use dom::window::Window;
|
||||
use dom_struct::dom_struct;
|
||||
use encoding_rs::{Encoding, UTF_8};
|
||||
use html5ever::{LocalName, Prefix};
|
||||
|
@ -337,10 +338,24 @@ impl HTMLFormElement {
|
|||
let scheme = action_components.scheme().to_owned();
|
||||
let enctype = submitter.enctype();
|
||||
let method = submitter.method();
|
||||
let _target = submitter.target();
|
||||
// TODO: Handle browsing contexts, partially loaded documents (step 16-17)
|
||||
|
||||
let mut load_data = LoadData::new(action_components, None, doc.get_referrer_policy(), Some(doc.url()));
|
||||
// Step 16, 17
|
||||
let target_attribute_value = submitter.target();
|
||||
let source = doc.browsing_context().unwrap();
|
||||
let (maybe_chosen, _new) = source.choose_browsing_context(target_attribute_value, false);
|
||||
let chosen = match maybe_chosen {
|
||||
Some(proxy) => proxy,
|
||||
None => return
|
||||
};
|
||||
let target_document = match chosen.document() {
|
||||
Some(doc) => doc,
|
||||
None => return
|
||||
};
|
||||
let target_window = target_document.window();
|
||||
let mut load_data = LoadData::new(action_components,
|
||||
None,
|
||||
target_document.get_referrer_policy(),
|
||||
Some(target_document.url()));
|
||||
|
||||
// Step 18
|
||||
match (&*scheme, method) {
|
||||
|
@ -351,17 +366,17 @@ impl HTMLFormElement {
|
|||
// https://html.spec.whatwg.org/multipage/#submit-mutate-action
|
||||
("http", FormMethod::FormGet) | ("https", FormMethod::FormGet) | ("data", FormMethod::FormGet) => {
|
||||
load_data.headers.set(ContentType::form_url_encoded());
|
||||
self.mutate_action_url(&mut form_data, load_data, encoding);
|
||||
self.mutate_action_url(&mut form_data, load_data, encoding, &target_window);
|
||||
}
|
||||
// https://html.spec.whatwg.org/multipage/#submit-body
|
||||
("http", FormMethod::FormPost) | ("https", FormMethod::FormPost) => {
|
||||
load_data.method = Method::Post;
|
||||
self.submit_entity_body(&mut form_data, load_data, enctype, encoding);
|
||||
self.submit_entity_body(&mut form_data, load_data, enctype, encoding, &target_window);
|
||||
}
|
||||
// https://html.spec.whatwg.org/multipage/#submit-get-action
|
||||
("file", _) | ("about", _) | ("data", FormMethod::FormPost) |
|
||||
("ftp", _) | ("javascript", _) => {
|
||||
self.plan_to_navigate(load_data);
|
||||
self.plan_to_navigate(load_data, &target_window);
|
||||
}
|
||||
("mailto", FormMethod::FormPost) => {
|
||||
// TODO: Mail as body
|
||||
|
@ -376,7 +391,11 @@ impl HTMLFormElement {
|
|||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#submit-mutate-action
|
||||
fn mutate_action_url(&self, form_data: &mut Vec<FormDatum>, mut load_data: LoadData, encoding: &'static Encoding) {
|
||||
fn mutate_action_url(&self,
|
||||
form_data: &mut Vec<FormDatum>,
|
||||
mut load_data: LoadData,
|
||||
encoding: &'static Encoding,
|
||||
target: &Window) {
|
||||
let charset = encoding.name();
|
||||
|
||||
self.set_encoding_override(load_data.url.as_mut_url().query_pairs_mut())
|
||||
|
@ -384,12 +403,16 @@ impl HTMLFormElement {
|
|||
.extend_pairs(form_data.into_iter()
|
||||
.map(|field| (field.name.clone(), field.replace_value(charset))));
|
||||
|
||||
self.plan_to_navigate(load_data);
|
||||
self.plan_to_navigate(load_data, target);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#submit-body
|
||||
fn submit_entity_body(&self, form_data: &mut Vec<FormDatum>, mut load_data: LoadData,
|
||||
enctype: FormEncType, encoding: &'static Encoding) {
|
||||
fn submit_entity_body(&self,
|
||||
form_data: &mut Vec<FormDatum>,
|
||||
mut load_data: LoadData,
|
||||
enctype: FormEncType,
|
||||
encoding: &'static Encoding,
|
||||
target: &Window) {
|
||||
let boundary = generate_boundary();
|
||||
let bytes = match enctype {
|
||||
FormEncType::UrlEncoded => {
|
||||
|
@ -415,7 +438,7 @@ impl HTMLFormElement {
|
|||
};
|
||||
|
||||
load_data.data = Some(bytes);
|
||||
self.plan_to_navigate(load_data);
|
||||
self.plan_to_navigate(load_data, target);
|
||||
}
|
||||
|
||||
fn set_encoding_override<'a>(&self, mut serializer: Serializer<UrlQuery<'a>>)
|
||||
|
@ -426,9 +449,7 @@ impl HTMLFormElement {
|
|||
}
|
||||
|
||||
/// [Planned navigation](https://html.spec.whatwg.org/multipage/#planned-navigation)
|
||||
fn plan_to_navigate(&self, load_data: LoadData) {
|
||||
let window = window_from_node(self);
|
||||
|
||||
fn plan_to_navigate(&self, load_data: LoadData, target: &Window) {
|
||||
// Step 1
|
||||
// Each planned navigation task is tagged with a generation ID, and
|
||||
// before the task is handled, it first checks whether the HTMLFormElement's
|
||||
|
@ -437,8 +458,8 @@ impl HTMLFormElement {
|
|||
self.generation_id.set(generation_id);
|
||||
|
||||
// Step 2.
|
||||
let pipeline_id = window.upcast::<GlobalScope>().pipeline_id();
|
||||
let script_chan = window.main_thread_script_chan().clone();
|
||||
let pipeline_id = target.upcast::<GlobalScope>().pipeline_id();
|
||||
let script_chan = target.main_thread_script_chan().clone();
|
||||
let this = Trusted::new(self);
|
||||
let task = task!(navigate_to_form_planned_navigation: move || {
|
||||
if generation_id != this.root().generation_id.get() {
|
||||
|
@ -452,7 +473,7 @@ impl HTMLFormElement {
|
|||
});
|
||||
|
||||
// Step 3.
|
||||
window.dom_manipulation_task_source().queue(task, window.upcast()).unwrap();
|
||||
target.dom_manipulation_task_source().queue(task, target.upcast()).unwrap();
|
||||
}
|
||||
|
||||
/// Interactively validate the constraints of form elements
|
||||
|
|
|
@ -40,8 +40,8 @@
|
|||
// https://github.com/whatwg/html/issues/2115
|
||||
[Replaceable] readonly attribute WindowProxy? parent;
|
||||
readonly attribute Element? frameElement;
|
||||
//WindowProxy open(optional DOMString url = "about:blank", optional DOMString target = "_blank",
|
||||
// optional DOMString features = "", optional boolean replace = false);
|
||||
WindowProxy? open(optional DOMString url = "about:blank", optional DOMString target = "_blank",
|
||||
optional DOMString features = "");
|
||||
//getter WindowProxy (unsigned long index);
|
||||
|
||||
// https://github.com/servo/servo/issues/14453
|
||||
|
|
|
@ -565,6 +565,15 @@ impl WindowMethods for Window {
|
|||
doc.abort();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-open
|
||||
fn Open(&self,
|
||||
url: DOMString,
|
||||
target: DOMString,
|
||||
features: DOMString)
|
||||
-> Option<DomRoot<WindowProxy>> {
|
||||
self.window_proxy().open(url, target, features)
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-window-closed
|
||||
fn Closed(&self) -> bool {
|
||||
self.window_proxy.get()
|
||||
|
|
|
@ -13,10 +13,12 @@ use dom::bindings::str::DOMString;
|
|||
use dom::bindings::trace::JSTraceable;
|
||||
use dom::bindings::utils::{WindowProxyHandler, get_array_index_from_id, AsVoidPtr};
|
||||
use dom::dissimilaroriginwindow::DissimilarOriginWindow;
|
||||
use dom::document::Document;
|
||||
use dom::element::Element;
|
||||
use dom::globalscope::GlobalScope;
|
||||
use dom::window::Window;
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::EmbedderMsg;
|
||||
use ipc_channel::ipc;
|
||||
use js::JSCLASS_IS_GLOBAL;
|
||||
use js::glue::{CreateWrapperProxyHandler, ProxyTraps};
|
||||
|
@ -42,7 +44,9 @@ use msg::constellation_msg::BrowsingContextId;
|
|||
use msg::constellation_msg::PipelineId;
|
||||
use msg::constellation_msg::TopLevelBrowsingContextId;
|
||||
use script_thread::ScriptThread;
|
||||
use script_traits::ScriptMsg;
|
||||
use script_traits::{AuxiliaryBrowsingContextLoadInfo, LoadData, NewLayoutInfo, ScriptMsg};
|
||||
use servo_config::prefs::PREFS;
|
||||
use servo_url::ServoUrl;
|
||||
use std::cell::Cell;
|
||||
use std::ptr;
|
||||
|
||||
|
@ -200,6 +204,138 @@ impl WindowProxy {
|
|||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#auxiliary-browsing-context
|
||||
fn create_auxiliary_browsing_context(&self, name: DOMString, _noopener: bool) -> Option<DomRoot<WindowProxy>> {
|
||||
let (chan, port) = ipc::channel().unwrap();
|
||||
let window = self.currently_active.get()
|
||||
.and_then(|id| ScriptThread::find_document(id))
|
||||
.and_then(|doc| Some(DomRoot::from_ref(doc.window())))
|
||||
.unwrap();
|
||||
let msg = EmbedderMsg::AllowOpeningBrowser(chan);
|
||||
window.send_to_embedder(msg);
|
||||
if port.recv().unwrap() {
|
||||
let new_top_level_browsing_context_id = TopLevelBrowsingContextId::new();
|
||||
let new_browsing_context_id = BrowsingContextId::from(new_top_level_browsing_context_id);
|
||||
let new_pipeline_id = PipelineId::new();
|
||||
let load_info = AuxiliaryBrowsingContextLoadInfo {
|
||||
opener_pipeline_id: self.currently_active.get().unwrap(),
|
||||
new_browsing_context_id: new_browsing_context_id,
|
||||
new_top_level_browsing_context_id: new_top_level_browsing_context_id,
|
||||
new_pipeline_id: new_pipeline_id,
|
||||
};
|
||||
let document = self.currently_active.get()
|
||||
.and_then(|id| ScriptThread::find_document(id))
|
||||
.unwrap();
|
||||
let blank_url = ServoUrl::parse("about:blank").ok().unwrap();
|
||||
let load_data = LoadData::new(blank_url,
|
||||
None,
|
||||
document.get_referrer_policy(),
|
||||
Some(document.url().clone()));
|
||||
let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap();
|
||||
let new_layout_info = NewLayoutInfo {
|
||||
parent_info: None,
|
||||
new_pipeline_id: new_pipeline_id,
|
||||
browsing_context_id: new_browsing_context_id,
|
||||
top_level_browsing_context_id: new_top_level_browsing_context_id,
|
||||
load_data: load_data,
|
||||
pipeline_port: pipeline_receiver,
|
||||
content_process_shutdown_chan: None,
|
||||
window_size: None,
|
||||
layout_threads: PREFS.get("layout.threads").as_u64().expect("count") as usize,
|
||||
};
|
||||
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);
|
||||
window.send_to_embedder(msg);
|
||||
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);
|
||||
}
|
||||
return Some(proxy)
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#window-open-steps
|
||||
pub fn open(&self,
|
||||
url: DOMString,
|
||||
target: DOMString,
|
||||
features: DOMString)
|
||||
-> Option<DomRoot<WindowProxy>> {
|
||||
// Step 3.
|
||||
let non_empty_target = match target.as_ref() {
|
||||
"" => DOMString::from("_blank"),
|
||||
_ => target
|
||||
};
|
||||
// TODO Step 4, properly tokenize features.
|
||||
// Step 5
|
||||
let noopener = features.contains("noopener");
|
||||
// Step 6, 7
|
||||
let (chosen, new) = match self.choose_browsing_context(non_empty_target, noopener) {
|
||||
(Some(chosen), new) => (chosen, new),
|
||||
(None, _) => return None
|
||||
};
|
||||
// TODO Step 8, set up browsing context features.
|
||||
let target_document = match chosen.document() {
|
||||
Some(target_document) => target_document,
|
||||
None => return None
|
||||
};
|
||||
let target_window = target_document.window();
|
||||
// Step 9, and 10.2, will have happened elsewhere,
|
||||
// since we've created a new browsing context and loaded it with about:blank.
|
||||
if !url.is_empty() {
|
||||
let existing_document = self.currently_active.get()
|
||||
.and_then(|id| ScriptThread::find_document(id)).unwrap();
|
||||
// Step 10.1
|
||||
let url = match existing_document.url().join(&url) {
|
||||
Ok(url) => url,
|
||||
Err(_) => return None, // TODO: throw a "SyntaxError" DOMException.
|
||||
};
|
||||
// Step 10.3
|
||||
target_window.load_url(url, new, false, target_document.get_referrer_policy());
|
||||
}
|
||||
if noopener {
|
||||
// Step 11 (Dis-owning has been done in create_auxiliary_browsing_context).
|
||||
return None
|
||||
}
|
||||
// Step 12.
|
||||
return target_document.browsing_context()
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
|
||||
pub fn choose_browsing_context(&self, name: DOMString, noopener: bool) -> (Option<DomRoot<WindowProxy>>, bool) {
|
||||
match name.to_lowercase().as_ref() {
|
||||
"" | "_self" => {
|
||||
// Step 3.
|
||||
(Some(DomRoot::from_ref(self)), false)
|
||||
},
|
||||
"_parent" => {
|
||||
// Step 4
|
||||
(Some(DomRoot::from_ref(self.parent().unwrap())), false)
|
||||
},
|
||||
"_top" => {
|
||||
// Step 5
|
||||
(Some(DomRoot::from_ref(self.top())), false)
|
||||
},
|
||||
"_blank" => {
|
||||
(self.create_auxiliary_browsing_context(name, noopener), true)
|
||||
},
|
||||
_ => {
|
||||
// Step 6.
|
||||
// TODO: expand the search to all 'familiar' bc,
|
||||
// including auxiliaries familiar by way of their opener.
|
||||
// See https://html.spec.whatwg.org/multipage/#familiar-with
|
||||
match ScriptThread::find_window_proxy_by_name(&name) {
|
||||
Some(proxy) => (Some(proxy), false),
|
||||
None => (self.create_auxiliary_browsing_context(name, noopener), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn discard_browsing_context(&self) {
|
||||
self.discarded.set(true);
|
||||
}
|
||||
|
@ -220,6 +356,11 @@ impl WindowProxy {
|
|||
self.frame_element.r()
|
||||
}
|
||||
|
||||
pub fn document(&self) -> Option<DomRoot<Document>> {
|
||||
self.currently_active.get()
|
||||
.and_then(|id| ScriptThread::find_document(id))
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<&WindowProxy> {
|
||||
self.parent.r()
|
||||
}
|
||||
|
|
|
@ -721,6 +721,18 @@ impl ScriptThread {
|
|||
}))
|
||||
}
|
||||
|
||||
pub fn find_window_proxy_by_name(name: &DOMString) -> Option<DomRoot<WindowProxy>> {
|
||||
SCRIPT_THREAD_ROOT.with(|root| root.get().and_then(|script_thread| {
|
||||
let script_thread = unsafe { &*script_thread };
|
||||
for (_, proxy) in script_thread.window_proxies.borrow().iter() {
|
||||
if proxy.get_name() == *name {
|
||||
return Some(DomRoot::from_ref(&**proxy))
|
||||
}
|
||||
}
|
||||
None
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn worklet_thread_pool() -> Rc<WorkletThreadPool> {
|
||||
SCRIPT_THREAD_ROOT.with(|root| {
|
||||
let script_thread = unsafe { &*root.get().unwrap() };
|
||||
|
|
|
@ -576,6 +576,19 @@ pub enum IFrameSandboxState {
|
|||
IFrameUnsandboxed,
|
||||
}
|
||||
|
||||
/// Specifies the information required to load an auxiliary browsing context.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AuxiliaryBrowsingContextLoadInfo {
|
||||
/// 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.
|
||||
pub new_pipeline_id: PipelineId,
|
||||
}
|
||||
|
||||
/// Specifies the information required to load an iframe.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct IFrameLoadInfo {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use AnimationState;
|
||||
use AuxiliaryBrowsingContextLoadInfo;
|
||||
use DocumentState;
|
||||
use IFrameLoadInfo;
|
||||
use IFrameLoadInfoWithData;
|
||||
|
@ -137,6 +138,8 @@ pub enum ScriptMsg {
|
|||
ScriptLoadedURLInIFrame(IFrameLoadInfoWithData),
|
||||
/// A load of the initial `about:blank` has been completed in an IFrame.
|
||||
ScriptNewIFrame(IFrameLoadInfo, IpcSender<LayoutControlMsg>),
|
||||
/// Script has opened a new auxiliary browsing context.
|
||||
ScriptNewAuxiliary(AuxiliaryBrowsingContextLoadInfo, IpcSender<LayoutControlMsg>),
|
||||
/// Requests that the constellation set the contents of the clipboard
|
||||
SetClipboardContents(String),
|
||||
/// Mark a new document as active
|
||||
|
@ -196,6 +199,7 @@ impl fmt::Debug for ScriptMsg {
|
|||
VisibilityChangeComplete(..) => "VisibilityChangeComplete",
|
||||
ScriptLoadedURLInIFrame(..) => "ScriptLoadedURLInIFrame",
|
||||
ScriptNewIFrame(..) => "ScriptNewIFrame",
|
||||
ScriptNewAuxiliary(..) => "ScriptNewAuxiliary",
|
||||
SetClipboardContents(..) => "SetClipboardContents",
|
||||
ActivateDocument => "ActivateDocument",
|
||||
SetDocumentState(..) => "SetDocumentState",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue