aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2016-11-22 16:41:46 -0600
committerGitHub <noreply@github.com>2016-11-22 16:41:46 -0600
commit1535f84bf19790c96c9f79b3f55264927b989b7c (patch)
treeaac5134c268e463321a96a0f12a7f1ccda15e5fc /components
parentbcf41844833e1818768f9d8ca73a931996617868 (diff)
parent7ae19c07f00d46662222fa21658a216ec49f3839 (diff)
downloadservo-1535f84bf19790c96c9f79b3f55264927b989b7c.tar.gz
servo-1535f84bf19790c96c9f79b3f55264927b989b7c.zip
Auto merge of #14211 - asajeffrey:constellation-share-more-script-threads, r=jdm
Share script threads by tab and by eTLD+1 <!-- Please describe your changes on the following line: --> This PR shares script threads among all similar-origin documents in the same tab. This allows DOM object to be shared among same-origin same-tab documents. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #633. - [X] These changes do not require tests because refactoring. <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14211) <!-- Reviewable:end -->
Diffstat (limited to 'components')
-rw-r--r--components/constellation/constellation.rs117
-rw-r--r--components/constellation/pipeline.rs29
-rw-r--r--components/script/script_thread.rs25
-rw-r--r--components/script_traits/lib.rs11
4 files changed, 102 insertions, 80 deletions
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index 52976bf1daf..8cb82a5cdd5 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -32,6 +32,7 @@ use msg::constellation_msg::{Key, KeyModifiers, KeyState};
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection};
use net_traits::{self, IpcSend, ResourceThreads};
use net_traits::image_cache_thread::ImageCacheThread;
+use net_traits::pub_domains::reg_suffix;
use net_traits::storage_thread::{StorageThreadMsg, StorageType};
use offscreen_gl_context::{GLContextAttributes, GLLimits};
use pipeline::{ChildProcess, InitialPipelineState, Pipeline};
@@ -54,7 +55,7 @@ use std::iter::once;
use std::marker::PhantomData;
use std::mem::replace;
use std::process;
-use std::rc::Rc;
+use std::rc::{Rc, Weak};
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use std::thread;
@@ -133,6 +134,11 @@ pub struct Constellation<Message, LTF, STF> {
/// to receive sw manager message
swmanager_receiver: Receiver<SWManagerMsg>,
+ /// A map from top-level frame id and registered domain name to script channels.
+ /// This double indirection ensures that separate tabs do not share script threads,
+ /// even if the same domain is loaded in each.
+ script_channels: HashMap<FrameId, HashMap<String, Weak<ScriptChan>>>,
+
/// A list of all the pipelines. (See the `pipeline` module for more details.)
pipelines: HashMap<PipelineId, Pipeline>,
@@ -484,6 +490,14 @@ fn log_entry(record: &LogRecord) -> Option<LogEntry> {
const WARNINGS_BUFFER_SIZE: usize = 32;
+/// The registered domain name (aka eTLD+1) for a URL.
+/// Returns None if the URL has no host name.
+/// Returns the registered suffix for the host name if it is a domain.
+/// Leaves the host name alone if it is an IP address.
+fn reg_host<'a>(url: &'a ServoUrl) -> Option<&'a str> {
+ url.domain().map(reg_suffix).or(url.host_str())
+}
+
impl<Message, LTF, STF> Constellation<Message, LTF, STF>
where LTF: LayoutThreadFactory<Message=Message>,
STF: ScriptThreadFactory<Message=Message>
@@ -523,6 +537,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
swmanager_chan: None,
swmanager_receiver: swmanager_receiver,
swmanager_sender: sw_mgr_clone,
+ script_channels: HashMap::new(),
pipelines: HashMap::new(),
frames: HashMap::new(),
pending_frames: vec!(),
@@ -584,26 +599,50 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
pipeline_id: PipelineId,
frame_id: FrameId,
parent_info: Option<(PipelineId, FrameType)>,
- old_pipeline_id: Option<PipelineId>,
initial_window_size: Option<TypedSize2D<f32, PagePx>>,
- script_channel: Option<Rc<ScriptChan>>,
load_data: LoadData,
+ sandbox: IFrameSandboxState,
is_private: bool) {
if self.shutting_down { return; }
+ // TODO: can we get a case where the child pipeline is created
+ // before the parent is part of the frame tree?
+ let top_level_frame_id = match parent_info {
+ Some((_, FrameType::MozBrowserIFrame)) => frame_id,
+ Some((parent_id, _)) => self.get_top_level_frame_for_pipeline(parent_id),
+ None => self.root_frame_id,
+ };
+
+ let (script_channel, host) = match sandbox {
+ IFrameSandboxState::IFrameSandboxed => (None, None),
+ IFrameSandboxState::IFrameUnsandboxed => match reg_host(&load_data.url) {
+ None => (None, None),
+ Some(host) => {
+ let script_channel = self.script_channels.get(&top_level_frame_id)
+ .and_then(|map| map.get(host))
+ .and_then(|weak| weak.upgrade());
+ match script_channel {
+ None => (None, Some(String::from(host))),
+ Some(script_channel) => (Some(script_channel.clone()), None),
+ }
+ },
+ },
+ };
+
let resource_threads = if is_private {
self.private_resource_threads.clone()
} else {
self.public_resource_threads.clone()
};
- let prev_visibility = if let Some(id) = old_pipeline_id {
- self.pipelines.get(&id).map(|pipeline| pipeline.visible)
- } else if let Some((parent_pipeline_id, _)) = parent_info {
- self.pipelines.get(&parent_pipeline_id).map(|pipeline| pipeline.visible)
- } else {
- None
- };
+ let parent_visibility = parent_info
+ .and_then(|(parent_pipeline_id, _)| self.pipelines.get(&parent_pipeline_id))
+ .map(|pipeline| pipeline.visible);
+
+ let prev_visibility = self.frames.get(&frame_id)
+ .and_then(|frame| self.pipelines.get(&frame.current.pipeline_id))
+ .map(|pipeline| pipeline.visible)
+ .or(parent_visibility);
// TODO: think about the case where the child pipeline is created
// before the parent is part of the frame tree.
@@ -649,6 +688,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.child_processes.push(child_process);
}
+ if let Some(host) = host {
+ self.script_channels.entry(top_level_frame_id)
+ .or_insert_with(HashMap::new)
+ .insert(host, Rc::downgrade(&pipeline.script_chan));
+ }
+
assert!(!self.pipelines.contains_key(&pipeline_id));
self.pipelines.insert(pipeline_id, pipeline);
}
@@ -1199,13 +1244,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// Notify the browser chrome that the pipeline has failed
self.trigger_mozbrowsererror(top_level_frame_id, reason, backtrace);
- let frame_id = FrameId::from(top_level_frame_id);
- let pipeline_id = self.frames.get(&frame_id).map(|frame| frame.current.pipeline_id);
+ let pipeline_id = self.frames.get(&top_level_frame_id).map(|frame| frame.current.pipeline_id);
let pipeline_url = pipeline_id.and_then(|id| self.pipelines.get(&id).map(|pipeline| pipeline.url.clone()));
let parent_info = pipeline_id.and_then(|id| self.pipelines.get(&id).and_then(|pipeline| pipeline.parent_info));
let window_size = pipeline_id.and_then(|id| self.pipelines.get(&id).and_then(|pipeline| pipeline.size));
- self.close_frame_children(frame_id, ExitPipelineMode::Force);
+ self.close_frame_children(top_level_frame_id, ExitPipelineMode::Force);
let failure_url = ServoUrl::parse("about:failure").expect("infallible");
@@ -1219,11 +1263,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let new_pipeline_id = PipelineId::new();
let load_data = LoadData::new(failure_url, None, None);
- self.new_pipeline(new_pipeline_id, frame_id, parent_info, pipeline_id,
- window_size, None, load_data, false);
-
+ let sandbox = IFrameSandboxState::IFrameSandboxed;
+ self.new_pipeline(new_pipeline_id, top_level_frame_id, parent_info, window_size, load_data, sandbox, false);
self.pending_frames.push(FrameChange {
- frame_id: frame_id,
+ frame_id: top_level_frame_id,
old_pipeline_id: pipeline_id,
new_pipeline_id: new_pipeline_id,
document_ready: false,
@@ -1252,8 +1295,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let window_size = self.window_size.visible_viewport;
let root_pipeline_id = PipelineId::new();
let root_frame_id = self.root_frame_id;
- self.new_pipeline(root_pipeline_id, root_frame_id, None, None, Some(window_size), None,
- LoadData::new(url.clone(), None, None), false);
+ let load_data = LoadData::new(url.clone(), None, None);
+ let sandbox = IFrameSandboxState::IFrameUnsandboxed;
+ self.new_pipeline(root_pipeline_id, root_frame_id, None, Some(window_size), load_data, sandbox, false);
self.handle_load_start_msg(root_pipeline_id);
self.pending_frames.push(FrameChange {
frame_id: self.root_frame_id,
@@ -1320,7 +1364,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// parent_pipeline_id's frame tree's children. This message is never the result of a
// page navigation.
fn handle_script_loaded_url_in_iframe_msg(&mut self, load_info: IFrameLoadInfo) {
- let (load_data, script_chan, window_size, is_private) = {
+ let (load_data, window_size, is_private) = {
let old_pipeline = load_info.old_pipeline_id
.and_then(|old_pipeline_id| self.pipelines.get(&old_pipeline_id));
@@ -1340,47 +1384,24 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
LoadData::new(url, None, None)
});
- // Compare the pipeline's url to the new url. If the origin is the same,
- // then reuse the script thread in creating the new pipeline
- let source_url = &source_pipeline.url;
-
let is_private = load_info.is_private || source_pipeline.is_private;
- // FIXME(#10968): this should probably match the origin check in
- // HTMLIFrameElement::contentDocument.
- let same_script = source_url.host() == load_data.url.host() &&
- source_url.port() == load_data.url.port() &&
- load_info.sandbox == IFrameSandboxState::IFrameUnsandboxed &&
- source_pipeline.is_private == is_private;
-
- // Reuse the script thread if the URL is same-origin
- let script_chan = if same_script {
- debug!("Constellation: loading same-origin iframe, \
- parent url {:?}, iframe url {:?}", source_url, load_data.url);
- Some(source_pipeline.script_chan.clone())
- } else {
- debug!("Constellation: loading cross-origin iframe, \
- parent url {:?}, iframe url {:?}", source_url, load_data.url);
- None
- };
-
let window_size = old_pipeline.and_then(|old_pipeline| old_pipeline.size);
if let Some(old_pipeline) = old_pipeline {
old_pipeline.freeze();
}
- (load_data, script_chan, window_size, is_private)
+ (load_data, window_size, is_private)
};
// Create the new pipeline, attached to the parent and push to pending frames
self.new_pipeline(load_info.new_pipeline_id,
load_info.frame_id,
- Some((load_info.parent_pipeline_id, load_info.frame_type)),
- load_info.old_pipeline_id,
+ Some((load_info.parent_pipeline_id, load_info.frame_type)),
window_size,
- script_chan,
load_data,
+ load_info.sandbox,
is_private);
self.pending_frames.push(FrameChange {
@@ -1517,7 +1538,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let window_size = self.pipelines.get(&source_id).and_then(|source| source.size);
let new_pipeline_id = PipelineId::new();
let root_frame_id = self.root_frame_id;
- self.new_pipeline(new_pipeline_id, root_frame_id, None, None, window_size, None, load_data, false);
+ let sandbox = IFrameSandboxState::IFrameUnsandboxed;
+ self.new_pipeline(new_pipeline_id, root_frame_id, None, window_size, load_data, sandbox, false);
self.pending_frames.push(FrameChange {
frame_id: root_frame_id,
old_pipeline_id: Some(source_id),
@@ -2272,6 +2294,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.close_frame_children(frame_id, exit_mode);
+ self.script_channels.remove(&frame_id);
if self.frames.remove(&frame_id).is_none() {
warn!("Closing frame {:?} twice.", frame_id);
}
diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs
index 44739fd6191..d77db028f2b 100644
--- a/components/constellation/pipeline.rs
+++ b/components/constellation/pipeline.rs
@@ -115,8 +115,9 @@ pub struct InitialPipelineState {
pub window_size: Option<TypedSize2D<f32, PagePx>>,
/// Information about the device pixel ratio.
pub device_pixel_ratio: ScaleFactor<f32, ViewportPx, DevicePixel>,
- /// A channel to the script thread, if applicable. If this is `Some`,
- /// then `parent_info` must also be `Some`.
+ /// A channel to the script thread, if applicable.
+ /// If this is `None`, create a new script thread.
+ /// If this is `Some`, then reuse an existing script thread.
pub script_chan: Option<Rc<ScriptChan>>,
/// Information about the page to load.
pub load_data: LoadData,
@@ -146,16 +147,23 @@ impl Pipeline {
let (layout_content_process_shutdown_chan, layout_content_process_shutdown_port) =
ipc::channel().expect("Pipeline layout content shutdown chan");
+ let device_pixel_ratio = state.device_pixel_ratio;
+ let window_size = state.window_size.map(|size| {
+ WindowSizeData {
+ visible_viewport: size,
+ initial_viewport: size * ScaleFactor::new(1.0),
+ device_pixel_ratio: device_pixel_ratio,
+ }
+ });
+
let (script_chan, content_ports) = match state.script_chan {
Some(script_chan) => {
- let (parent_pipeline_id, frame_type) =
- state.parent_info.expect("script_pipeline != None but parent_info == None");
let new_layout_info = NewLayoutInfo {
- parent_pipeline_id: parent_pipeline_id,
+ parent_info: state.parent_info,
new_pipeline_id: state.id,
frame_id: state.frame_id,
- frame_type: frame_type,
load_data: state.load_data.clone(),
+ window_size: window_size,
pipeline_port: pipeline_port,
layout_to_constellation_chan: state.layout_to_constellation_chan.clone(),
content_process_shutdown_chan: layout_content_process_shutdown_chan.clone(),
@@ -191,15 +199,6 @@ impl Pipeline {
script_to_devtools_chan
});
- let device_pixel_ratio = state.device_pixel_ratio;
- let window_size = state.window_size.map(|size| {
- WindowSizeData {
- visible_viewport: size,
- initial_viewport: size * ScaleFactor::new(1.0),
- device_pixel_ratio: device_pixel_ratio,
- }
- });
-
let (script_content_process_shutdown_chan, script_content_process_shutdown_port) =
ipc::channel().expect("Pipeline script content process shutdown chan");
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 8a5fd03c479..135fccf58c3 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -1173,11 +1173,11 @@ impl ScriptThread {
fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) {
let NewLayoutInfo {
- parent_pipeline_id,
+ parent_info,
new_pipeline_id,
frame_id,
- frame_type,
load_data,
+ window_size,
pipeline_port,
layout_to_constellation_chan,
content_process_shutdown_chan,
@@ -1187,7 +1187,7 @@ impl ScriptThread {
let layout_pair = channel();
let layout_chan = layout_pair.0.clone();
- let layout_creation_info = NewLayoutThreadInfo {
+ let msg = message::Msg::CreateLayoutThread(NewLayoutThreadInfo {
id: new_pipeline_id,
url: load_data.url.clone(),
is_parent: false,
@@ -1198,19 +1198,18 @@ impl ScriptThread {
image_cache_thread: self.image_cache_thread.clone(),
content_process_shutdown_chan: content_process_shutdown_chan,
layout_threads: layout_threads,
- };
-
- let parent_window = self.documents.borrow().find_window(parent_pipeline_id)
- .expect("ScriptThread: received a layout for a parent pipeline not in this script thread. This is a bug.");
+ });
- // Tell layout to actually spawn the thread.
- parent_window.layout_chan()
- .send(message::Msg::CreateLayoutThread(layout_creation_info))
- .unwrap();
+ // Pick a layout thread, any layout thread
+ match self.documents.borrow().iter().next() {
+ None => panic!("Layout attached to empty script thread."),
+ // Tell the layout thread factory to actually spawn the thread.
+ Some((_, document)) => document.window().layout_chan().send(msg).unwrap(),
+ };
// Kick off the fetch for the new resource.
- let new_load = InProgressLoad::new(new_pipeline_id, frame_id, Some((parent_pipeline_id, frame_type)),
- layout_chan, parent_window.window_size(),
+ let new_load = InProgressLoad::new(new_pipeline_id, frame_id, parent_info,
+ layout_chan, window_size,
load_data.url.clone());
self.start_page_load(new_load, load_data);
}
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index 8a437ff24ee..458972283db 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -162,19 +162,20 @@ impl LoadData {
}
}
-/// The initial data associated with a newly-created framed pipeline.
+/// The initial data required to create a new layout attached to an existing script thread.
#[derive(Deserialize, Serialize)]
pub struct NewLayoutInfo {
- /// Id of the parent of this new pipeline.
- pub parent_pipeline_id: PipelineId,
+ /// The ID of the parent pipeline and frame type, if any.
+ /// If `None`, this is a root pipeline.
+ pub parent_info: Option<(PipelineId, FrameType)>,
/// Id of the newly-created pipeline.
pub new_pipeline_id: PipelineId,
/// Id of the frame associated with this pipeline.
pub frame_id: FrameId,
- /// Type of the frame associated with this pipeline.
- pub frame_type: FrameType,
/// Network request data which will be initiated by the script thread.
pub load_data: LoadData,
+ /// Information about the initial window size.
+ pub window_size: Option<WindowSizeData>,
/// A port on which layout can receive messages from the pipeline.
pub pipeline_port: IpcReceiver<LayoutControlMsg>,
/// A sender for the layout thread to communicate to the constellation.