Propagate privacy information of iframes to corresponding pipelines. Make iframes of differing privacy values be considered cross-origin.

Make the constellation hand out separate private and public channels for the pipeline content to communicate with the resource thread as necessary.
This commit is contained in:
Sagar Muchhal 2016-04-18 16:29:24 -04:00 committed by Josh Matthews
parent a5778fb5da
commit 7e2b4d163b
14 changed files with 294 additions and 138 deletions

View file

@ -108,7 +108,10 @@ pub struct Constellation<Message, LTF, STF> {
compositor_proxy: Box<CompositorProxy>, compositor_proxy: Box<CompositorProxy>,
/// Channels through which messages can be sent to the resource-related threads. /// Channels through which messages can be sent to the resource-related threads.
resource_threads: ResourceThreads, public_resource_threads: ResourceThreads,
/// Channels through which messages can be sent to the resource-related threads.
private_resource_threads: ResourceThreads,
/// A channel through which messages can be sent to the image cache thread. /// A channel through which messages can be sent to the image cache thread.
image_cache_thread: ImageCacheThread, image_cache_thread: ImageCacheThread,
@ -201,7 +204,9 @@ pub struct InitialConstellationState {
/// A channel to the font cache thread. /// A channel to the font cache thread.
pub font_cache_thread: FontCacheThread, pub font_cache_thread: FontCacheThread,
/// A channel to the resource thread. /// A channel to the resource thread.
pub resource_threads: ResourceThreads, pub public_resource_threads: ResourceThreads,
/// A channel to the resource thread.
pub private_resource_threads: ResourceThreads,
/// A channel to the time profiler thread. /// A channel to the time profiler thread.
pub time_profiler_chan: time::ProfilerChan, pub time_profiler_chan: time::ProfilerChan,
/// A channel to the memory profiler thread. /// A channel to the memory profiler thread.
@ -331,7 +336,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
compositor_proxy: state.compositor_proxy, compositor_proxy: state.compositor_proxy,
devtools_chan: state.devtools_chan, devtools_chan: state.devtools_chan,
bluetooth_thread: state.bluetooth_thread, bluetooth_thread: state.bluetooth_thread,
resource_threads: state.resource_threads, public_resource_threads: state.public_resource_threads,
private_resource_threads: state.private_resource_threads,
image_cache_thread: state.image_cache_thread, image_cache_thread: state.image_cache_thread,
font_cache_thread: state.font_cache_thread, font_cache_thread: state.font_cache_thread,
pipelines: HashMap::new(), pipelines: HashMap::new(),
@ -404,9 +410,16 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
parent_info: Option<(PipelineId, SubpageId, FrameType)>, parent_info: Option<(PipelineId, SubpageId, FrameType)>,
initial_window_size: Option<TypedSize2D<PagePx, f32>>, initial_window_size: Option<TypedSize2D<PagePx, f32>>,
script_channel: Option<IpcSender<ConstellationControlMsg>>, script_channel: Option<IpcSender<ConstellationControlMsg>>,
load_data: LoadData) { load_data: LoadData,
is_private: bool) {
if self.shutting_down { return; } if self.shutting_down { return; }
let resource_threads = if is_private {
self.private_resource_threads.clone()
} else {
self.public_resource_threads.clone()
};
let parent_visibility = if let Some((parent_pipeline_id, _, _)) = parent_info { let parent_visibility = if let Some((parent_pipeline_id, _, _)) = parent_info {
self.pipelines.get(&parent_pipeline_id).map(|pipeline| pipeline.visible) self.pipelines.get(&parent_pipeline_id).map(|pipeline| pipeline.visible)
} else { } else {
@ -425,7 +438,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
bluetooth_thread: self.bluetooth_thread.clone(), bluetooth_thread: self.bluetooth_thread.clone(),
image_cache_thread: self.image_cache_thread.clone(), image_cache_thread: self.image_cache_thread.clone(),
font_cache_thread: self.font_cache_thread.clone(), font_cache_thread: self.font_cache_thread.clone(),
resource_threads: self.resource_threads.clone(), resource_threads: resource_threads,
time_profiler_chan: self.time_profiler_chan.clone(), time_profiler_chan: self.time_profiler_chan.clone(),
mem_profiler_chan: self.mem_profiler_chan.clone(), mem_profiler_chan: self.mem_profiler_chan.clone(),
window_size: initial_window_size, window_size: initial_window_size,
@ -435,6 +448,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
pipeline_namespace_id: self.next_pipeline_namespace_id(), pipeline_namespace_id: self.next_pipeline_namespace_id(),
parent_visibility: parent_visibility, parent_visibility: parent_visibility,
webrender_api_sender: self.webrender_api_sender.clone(), webrender_api_sender: self.webrender_api_sender.clone(),
is_private: is_private,
}); });
let (pipeline, child_process) = match result { let (pipeline, child_process) = match result {
@ -850,7 +864,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.image_cache_thread.exit(); self.image_cache_thread.exit();
debug!("Exiting core resource threads."); debug!("Exiting core resource threads.");
if let Err(e) = self.resource_threads.send(net_traits::CoreResourceMsg::Exit(core_sender)) { if let Err(e) = self.public_resource_threads.send(net_traits::CoreResourceMsg::Exit(core_sender)) {
warn!("Exit resource thread failed ({})", e); warn!("Exit resource thread failed ({})", e);
} }
@ -863,12 +877,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
} }
debug!("Exiting storage resource threads."); debug!("Exiting storage resource threads.");
if let Err(e) = self.resource_threads.send(StorageThreadMsg::Exit(storage_sender)) { if let Err(e) = self.public_resource_threads.send(StorageThreadMsg::Exit(storage_sender)) {
warn!("Exit storage thread failed ({})", e); warn!("Exit storage thread failed ({})", e);
} }
debug!("Exiting file manager resource threads."); debug!("Exiting file manager resource threads.");
if let Err(e) = self.resource_threads.send(FileManagerThreadMsg::Exit) { if let Err(e) = self.public_resource_threads.send(FileManagerThreadMsg::Exit) {
warn!("Exit storage thread failed ({})", e); warn!("Exit storage thread failed ({})", e);
} }
@ -951,7 +965,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let new_pipeline_id = PipelineId::new(); let new_pipeline_id = PipelineId::new();
let load_data = LoadData::new(failure_url, None, None); let load_data = LoadData::new(failure_url, None, None);
self.new_pipeline(new_pipeline_id, parent_info, window_size, None, load_data); self.new_pipeline(new_pipeline_id, parent_info, window_size, None, load_data, false);
self.push_pending_frame(new_pipeline_id, Some(pipeline_id)); self.push_pending_frame(new_pipeline_id, Some(pipeline_id));
@ -965,7 +979,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let root_pipeline_id = PipelineId::new(); let root_pipeline_id = PipelineId::new();
debug_assert!(PipelineId::fake_root_pipeline_id() == root_pipeline_id); debug_assert!(PipelineId::fake_root_pipeline_id() == root_pipeline_id);
self.new_pipeline(root_pipeline_id, None, Some(window_size), None, self.new_pipeline(root_pipeline_id, None, Some(window_size), None,
LoadData::new(url.clone(), None, None)); LoadData::new(url.clone(), None, None), false);
self.handle_load_start_msg(&root_pipeline_id); self.handle_load_start_msg(&root_pipeline_id);
self.push_pending_frame(root_pipeline_id, None); self.push_pending_frame(root_pipeline_id, None);
self.compositor_proxy.send(ToCompositorMsg::ChangePageUrl(root_pipeline_id, url)); self.compositor_proxy.send(ToCompositorMsg::ChangePageUrl(root_pipeline_id, url));
@ -1030,7 +1044,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
.and_then(|old_subpage_id| self.subpage_map.get(&(load_info.containing_pipeline_id, old_subpage_id))) .and_then(|old_subpage_id| self.subpage_map.get(&(load_info.containing_pipeline_id, old_subpage_id)))
.cloned(); .cloned();
let (load_data, script_chan, window_size) = { let (load_data, script_chan, window_size, is_private) = {
let old_pipeline = old_pipeline_id let old_pipeline = old_pipeline_id
.and_then(|old_pipeline_id| self.pipelines.get(&old_pipeline_id)); .and_then(|old_pipeline_id| self.pipelines.get(&old_pipeline_id));
@ -1054,11 +1068,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// then reuse the script thread in creating the new pipeline // then reuse the script thread in creating the new pipeline
let source_url = &source_pipeline.url; 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 // FIXME(#10968): this should probably match the origin check in
// HTMLIFrameElement::contentDocument. // HTMLIFrameElement::contentDocument.
let same_script = source_url.host() == load_data.url.host() && let same_script = source_url.host() == load_data.url.host() &&
source_url.port() == load_data.url.port() && source_url.port() == load_data.url.port() &&
load_info.sandbox == IFrameSandboxState::IFrameUnsandboxed; load_info.sandbox == IFrameSandboxState::IFrameUnsandboxed &&
source_pipeline.is_private == is_private;
// Reuse the script thread if the URL is same-origin // Reuse the script thread if the URL is same-origin
let script_chan = if same_script { let script_chan = if same_script {
@ -1077,16 +1094,17 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
old_pipeline.freeze(); old_pipeline.freeze();
} }
(load_data, script_chan, window_size) (load_data, script_chan, window_size, is_private)
}; };
// Create the new pipeline, attached to the parent and push to pending frames // Create the new pipeline, attached to the parent and push to pending frames
self.new_pipeline(load_info.new_pipeline_id, self.new_pipeline(load_info.new_pipeline_id,
Some((load_info.containing_pipeline_id, load_info.new_subpage_id, load_info.frame_type)), Some((load_info.containing_pipeline_id, load_info.new_subpage_id, load_info.frame_type)),
window_size, window_size,
script_chan, script_chan,
load_data); load_data,
is_private);
self.subpage_map.insert((load_info.containing_pipeline_id, load_info.new_subpage_id), self.subpage_map.insert((load_info.containing_pipeline_id, load_info.new_subpage_id),
load_info.new_pipeline_id); load_info.new_pipeline_id);
@ -1217,7 +1235,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// Create the new pipeline // Create the new pipeline
let window_size = self.pipelines.get(&source_id).and_then(|source| source.size); let window_size = self.pipelines.get(&source_id).and_then(|source| source.size);
let new_pipeline_id = PipelineId::new(); let new_pipeline_id = PipelineId::new();
self.new_pipeline(new_pipeline_id, None, window_size, None, load_data); self.new_pipeline(new_pipeline_id, None, window_size, None, load_data, false);
self.push_pending_frame(new_pipeline_id, Some(source_id)); self.push_pending_frame(new_pipeline_id, Some(source_id));
// Send message to ScriptThread that will suspend all timers // Send message to ScriptThread that will suspend all timers
@ -1906,25 +1924,6 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
ReadyToSave::Ready ReadyToSave::Ready
} }
/// Checks whether the pipeline or its ancestors are private
#[allow(dead_code)]
fn check_is_pipeline_private(&self, mut pipeline_id: PipelineId) -> bool {
loop {
match self.pipelines.get(&pipeline_id) {
Some(pipeline) if pipeline.is_private => return true,
Some(pipeline) => match pipeline.parent_info {
None => return false,
Some((_, _, FrameType::MozBrowserIFrame)) => return false,
Some((parent_id, _, _)) => pipeline_id = parent_id,
},
None => {
warn!("Finding private ancestor for pipeline {} after closure.", pipeline_id);
return false;
},
}
}
}
// Close a frame (and all children) // Close a frame (and all children)
fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) { fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) {
// Store information about the pipelines to be closed. Then close the // Store information about the pipelines to be closed. Then close the

View file

@ -64,6 +64,7 @@ pub struct Pipeline {
/// animations cause composites to be continually scheduled. /// animations cause composites to be continually scheduled.
pub running_animations: bool, pub running_animations: bool,
pub children: Vec<FrameId>, pub children: Vec<FrameId>,
/// Whether this pipeline is considered distinct from public pipelines.
pub is_private: bool, pub is_private: bool,
/// Whether this pipeline should be treated as visible for the purposes of scheduling and /// Whether this pipeline should be treated as visible for the purposes of scheduling and
/// resource management. /// resource management.
@ -119,6 +120,8 @@ pub struct InitialPipelineState {
pub parent_visibility: Option<bool>, pub parent_visibility: Option<bool>,
/// Optional webrender api (if enabled). /// Optional webrender api (if enabled).
pub webrender_api_sender: Option<webrender_traits::RenderApiSender>, pub webrender_api_sender: Option<webrender_traits::RenderApiSender>,
/// Whether this pipeline is considered private.
pub is_private: bool,
} }
impl Pipeline { impl Pipeline {
@ -254,6 +257,7 @@ impl Pipeline {
pipeline_chan, pipeline_chan,
state.compositor_proxy, state.compositor_proxy,
chrome_to_paint_chan, chrome_to_paint_chan,
state.is_private,
state.load_data.url, state.load_data.url,
state.window_size, state.window_size,
state.parent_visibility.unwrap_or(true)); state.parent_visibility.unwrap_or(true));
@ -269,6 +273,7 @@ impl Pipeline {
layout_chan: IpcSender<LayoutControlMsg>, layout_chan: IpcSender<LayoutControlMsg>,
compositor_proxy: Box<CompositorProxy + 'static + Send>, compositor_proxy: Box<CompositorProxy + 'static + Send>,
chrome_to_paint_chan: Sender<ChromeToPaintMsg>, chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
is_private: bool,
url: Url, url: Url,
size: Option<TypedSize2D<PagePx, f32>>, size: Option<TypedSize2D<PagePx, f32>>,
visible: bool) visible: bool)
@ -285,8 +290,8 @@ impl Pipeline {
children: vec!(), children: vec!(),
size: size, size: size,
running_animations: false, running_animations: false,
is_private: false,
visible: visible, visible: visible,
is_private: is_private,
} }
} }

View file

@ -19,11 +19,13 @@ use http_loader::{self, HttpState};
use hyper::client::pool::Pool; use hyper::client::pool::Pool;
use hyper::header::{ContentType, Header, SetCookie}; use hyper::header::{ContentType, Header, SetCookie};
use hyper::mime::{Mime, SubLevel, TopLevel}; use hyper::mime::{Mime, SubLevel, TopLevel};
use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcReceiverSet};
use mime_classifier::{ApacheBugFlag, MIMEClassifier, NoSniffFlag}; use mime_classifier::{ApacheBugFlag, MIMEClassifier, NoSniffFlag};
use net_traits::LoadContext; use net_traits::LoadContext;
use net_traits::ProgressMsg::Done; use net_traits::ProgressMsg::Done;
use net_traits::filemanager_thread::FileManagerThreadMsg;
use net_traits::request::{Request, RequestInit}; use net_traits::request::{Request, RequestInit};
use net_traits::storage_thread::StorageThreadMsg;
use net_traits::{AsyncResponseTarget, Metadata, ProgressMsg, ResponseAction, CoreResourceThread}; use net_traits::{AsyncResponseTarget, Metadata, ProgressMsg, ResponseAction, CoreResourceThread};
use net_traits::{CoreResourceMsg, CookieSource, FetchResponseMsg, FetchTaskTarget, LoadConsumer}; use net_traits::{CoreResourceMsg, CookieSource, FetchResponseMsg, FetchTaskTarget, LoadConsumer};
use net_traits::{LoadData, LoadResponse, NetworkError, ResourceId}; use net_traits::{LoadData, LoadResponse, NetworkError, ResourceId};
@ -56,6 +58,14 @@ pub enum ProgressSender {
Listener(AsyncResponseTarget), Listener(AsyncResponseTarget),
} }
#[derive(Clone)]
pub struct ResourceGroup {
cookie_jar: Arc<RwLock<CookieStorage>>,
auth_cache: Arc<RwLock<AuthCache>>,
hsts_list: Arc<RwLock<HstsList>>,
connector: Arc<Pool<Connector>>,
}
impl ProgressSender { impl ProgressSender {
//XXXjdm return actual error //XXXjdm return actual error
pub fn send(&self, msg: ProgressMsg) -> Result<(), ()> { pub fn send(&self, msg: ProgressMsg) -> Result<(), ()> {
@ -158,56 +168,117 @@ fn start_sending_opt(start_chan: LoadConsumer, metadata: Metadata,
} }
} }
/// Returns a tuple of (public, private) senders to the new threads.
pub fn new_resource_threads(user_agent: String, pub fn new_resource_threads(user_agent: String,
devtools_chan: Option<Sender<DevtoolsControlMsg>>, devtools_chan: Option<Sender<DevtoolsControlMsg>>,
profiler_chan: ProfilerChan) -> ResourceThreads { profiler_chan: ProfilerChan) -> (ResourceThreads, ResourceThreads) {
ResourceThreads::new(new_core_resource_thread(user_agent, devtools_chan, profiler_chan), let (public_core, private_core) = new_core_resource_thread(user_agent, devtools_chan, profiler_chan);
StorageThreadFactory::new(), let storage: IpcSender<StorageThreadMsg> = StorageThreadFactory::new();
FileManagerThreadFactory::new(TFD_PROVIDER)) let filemanager: IpcSender<FileManagerThreadMsg> = FileManagerThreadFactory::new(TFD_PROVIDER);
(ResourceThreads::new(public_core, storage.clone(), filemanager.clone()),
ResourceThreads::new(private_core, storage, filemanager))
} }
/// Create a CoreResourceThread /// Create a CoreResourceThread
pub fn new_core_resource_thread(user_agent: String, pub fn new_core_resource_thread(user_agent: String,
devtools_chan: Option<Sender<DevtoolsControlMsg>>, devtools_chan: Option<Sender<DevtoolsControlMsg>>,
profiler_chan: ProfilerChan) -> CoreResourceThread { profiler_chan: ProfilerChan)
let hsts_preload = HstsList::from_servo_preload(); -> (CoreResourceThread, CoreResourceThread) {
let (setup_chan, setup_port) = ipc::channel().unwrap(); let (public_setup_chan, public_setup_port) = ipc::channel().unwrap();
let setup_chan_clone = setup_chan.clone(); let (private_setup_chan, private_setup_port) = ipc::channel().unwrap();
let public_setup_chan_clone = public_setup_chan.clone();
let private_setup_chan_clone = private_setup_chan.clone();
spawn_named("ResourceManager".to_owned(), move || { spawn_named("ResourceManager".to_owned(), move || {
let resource_manager = CoreResourceManager::new( let resource_manager = CoreResourceManager::new(
user_agent, hsts_preload, devtools_chan, profiler_chan user_agent, devtools_chan, profiler_chan
); );
let mut channel_manager = ResourceChannelManager { let mut channel_manager = ResourceChannelManager {
from_client: setup_port, resource_manager: resource_manager,
resource_manager: resource_manager
}; };
channel_manager.start(setup_chan_clone); channel_manager.start(public_setup_chan_clone,
private_setup_chan_clone,
public_setup_port,
private_setup_port);
}); });
setup_chan (public_setup_chan, private_setup_chan)
} }
struct ResourceChannelManager { struct ResourceChannelManager {
from_client: IpcReceiver<CoreResourceMsg>, resource_manager: CoreResourceManager,
resource_manager: CoreResourceManager }
fn create_resource_groups() -> (ResourceGroup, ResourceGroup) {
let mut hsts_list = HstsList::from_servo_preload();
let mut auth_cache = AuthCache::new();
let mut cookie_jar = CookieStorage::new();
if let Some(ref config_dir) = opts::get().config_dir {
read_json_from_file(&mut auth_cache, config_dir, "auth_cache.json");
read_json_from_file(&mut hsts_list, config_dir, "hsts_list.json");
read_json_from_file(&mut cookie_jar, config_dir, "cookie_jar.json");
}
let resource_group = ResourceGroup {
cookie_jar: Arc::new(RwLock::new(cookie_jar)),
auth_cache: Arc::new(RwLock::new(auth_cache)),
hsts_list: Arc::new(RwLock::new(hsts_list.clone())),
connector: create_http_connector(),
};
let private_resource_group = ResourceGroup {
cookie_jar: Arc::new(RwLock::new(CookieStorage::new())),
auth_cache: Arc::new(RwLock::new(AuthCache::new())),
hsts_list: Arc::new(RwLock::new(HstsList::new())),
connector: create_http_connector(),
};
(resource_group, private_resource_group)
} }
impl ResourceChannelManager { impl ResourceChannelManager {
fn start(&mut self, control_sender: CoreResourceThread) { #[allow(unsafe_code)]
fn start(&mut self,
public_control_sender: CoreResourceThread,
private_control_sender: CoreResourceThread,
public_receiver: IpcReceiver<CoreResourceMsg>,
private_receiver: IpcReceiver<CoreResourceMsg>) {
let (public_resource_group, private_resource_group) = create_resource_groups();
let mut rx_set = IpcReceiverSet::new().unwrap();
let private_id = rx_set.add(private_receiver).unwrap();
let public_id = rx_set.add(public_receiver).unwrap();
loop { loop {
match self.from_client.recv().unwrap() { for (id, data) in rx_set.select().unwrap().into_iter().map(|m| m.unwrap()) {
let (group, sender) = if id == private_id {
(&private_resource_group, &private_control_sender)
} else {
assert_eq!(id, public_id);
(&public_resource_group, &public_control_sender)
};
if let Ok(msg) = data.to() {
if !self.process_msg(msg, group, &sender) {
break;
}
}
}
}
}
/// Returns false if the thread should exit.
fn process_msg(&mut self,
msg: CoreResourceMsg,
group: &ResourceGroup,
control_sender: &CoreResourceThread) -> bool {
match msg {
CoreResourceMsg::Load(load_data, consumer, id_sender) => CoreResourceMsg::Load(load_data, consumer, id_sender) =>
self.resource_manager.load(load_data, consumer, id_sender, control_sender.clone()), self.resource_manager.load(load_data, consumer, id_sender, control_sender.clone(), group),
CoreResourceMsg::Fetch(init, sender) => CoreResourceMsg::Fetch(init, sender) =>
self.resource_manager.fetch(init, sender), self.resource_manager.fetch(init, sender, group),
CoreResourceMsg::WebsocketConnect(connect, connect_data) => CoreResourceMsg::WebsocketConnect(connect, connect_data) =>
self.resource_manager.websocket_connect(connect, connect_data), self.resource_manager.websocket_connect(connect, connect_data, group),
CoreResourceMsg::SetCookiesForUrl(request, cookie_list, source) => CoreResourceMsg::SetCookiesForUrl(request, cookie_list, source) =>
self.resource_manager.set_cookies_for_url(request, cookie_list, source), self.resource_manager.set_cookies_for_url(request, cookie_list, source, group),
CoreResourceMsg::GetCookiesForUrl(url, consumer, source) => { CoreResourceMsg::GetCookiesForUrl(url, consumer, source) => {
let cookie_jar = &self.resource_manager.cookie_jar; let mut cookie_jar = group.cookie_jar.write().unwrap();
let mut cookie_jar = cookie_jar.write().unwrap();
consumer.send(cookie_jar.cookies_for_url(&url, source)).unwrap(); consumer.send(cookie_jar.cookies_for_url(&url, source)).unwrap();
} }
CoreResourceMsg::Cancel(res_id) => { CoreResourceMsg::Cancel(res_id) => {
@ -221,25 +292,24 @@ impl ResourceChannelManager {
} }
CoreResourceMsg::Exit(sender) => { CoreResourceMsg::Exit(sender) => {
if let Some(ref config_dir) = opts::get().config_dir { if let Some(ref config_dir) = opts::get().config_dir {
match self.resource_manager.auth_cache.read() { match group.auth_cache.read() {
Ok(auth_cache) => write_json_to_file(&*auth_cache, config_dir, "auth_cache.json"), Ok(auth_cache) => write_json_to_file(&*auth_cache, config_dir, "auth_cache.json"),
Err(_) => warn!("Error writing auth cache to disk"), Err(_) => warn!("Error writing auth cache to disk"),
} }
match self.resource_manager.cookie_jar.read() { match group.cookie_jar.read() {
Ok(jar) => write_json_to_file(&*jar, config_dir, "cookie_jar.json"), Ok(jar) => write_json_to_file(&*jar, config_dir, "cookie_jar.json"),
Err(_) => warn!("Error writing cookie jar to disk"), Err(_) => warn!("Error writing cookie jar to disk"),
} }
match self.resource_manager.hsts_list.read() { match group.hsts_list.read() {
Ok(hsts) => write_json_to_file(&*hsts, config_dir, "hsts_list.json"), Ok(hsts) => write_json_to_file(&*hsts, config_dir, "hsts_list.json"),
Err(_) => warn!("Error writing hsts list to disk"), Err(_) => warn!("Error writing hsts list to disk"),
} }
} }
let _ = sender.send(()); let _ = sender.send(());
break; return false;
}
} }
} }
true
} }
} }
@ -381,50 +451,37 @@ pub struct AuthCache {
pub struct CoreResourceManager { pub struct CoreResourceManager {
user_agent: String, user_agent: String,
cookie_jar: Arc<RwLock<CookieStorage>>,
auth_cache: Arc<RwLock<AuthCache>>,
mime_classifier: Arc<MIMEClassifier>, mime_classifier: Arc<MIMEClassifier>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>, devtools_chan: Option<Sender<DevtoolsControlMsg>>,
profiler_chan: ProfilerChan, profiler_chan: ProfilerChan,
hsts_list: Arc<RwLock<HstsList>>,
connector: Arc<Pool<Connector>>,
cancel_load_map: HashMap<ResourceId, Sender<()>>, cancel_load_map: HashMap<ResourceId, Sender<()>>,
next_resource_id: ResourceId, next_resource_id: ResourceId,
} }
impl CoreResourceManager { impl CoreResourceManager {
pub fn new(user_agent: String, pub fn new(user_agent: String,
mut hsts_list: HstsList,
devtools_channel: Option<Sender<DevtoolsControlMsg>>, devtools_channel: Option<Sender<DevtoolsControlMsg>>,
profiler_chan: ProfilerChan) -> CoreResourceManager { profiler_chan: ProfilerChan) -> CoreResourceManager {
let mut auth_cache = AuthCache::new();
let mut cookie_jar = CookieStorage::new();
if let Some(ref config_dir) = opts::get().config_dir {
read_json_from_file(&mut auth_cache, config_dir, "auth_cache.json");
read_json_from_file(&mut hsts_list, config_dir, "hsts_list.json");
read_json_from_file(&mut cookie_jar, config_dir, "cookie_jar.json");
}
CoreResourceManager { CoreResourceManager {
user_agent: user_agent, user_agent: user_agent,
cookie_jar: Arc::new(RwLock::new(cookie_jar)),
auth_cache: Arc::new(RwLock::new(auth_cache)),
mime_classifier: Arc::new(MIMEClassifier::new()), mime_classifier: Arc::new(MIMEClassifier::new()),
devtools_chan: devtools_channel, devtools_chan: devtools_channel,
profiler_chan: profiler_chan, profiler_chan: profiler_chan,
hsts_list: Arc::new(RwLock::new(hsts_list)),
connector: create_http_connector(),
cancel_load_map: HashMap::new(), cancel_load_map: HashMap::new(),
next_resource_id: ResourceId(0), next_resource_id: ResourceId(0),
} }
} }
fn set_cookies_for_url(&mut self, request: Url, cookie_list: String, source: CookieSource) { fn set_cookies_for_url(&mut self,
request: Url,
cookie_list: String,
source: CookieSource,
resource_group: &ResourceGroup) {
let header = Header::parse_header(&[cookie_list.into_bytes()]); let header = Header::parse_header(&[cookie_list.into_bytes()]);
if let Ok(SetCookie(cookies)) = header { if let Ok(SetCookie(cookies)) = header {
for bare_cookie in cookies { for bare_cookie in cookies {
if let Some(cookie) = cookie::Cookie::new_wrapped(bare_cookie, &request, source) { if let Some(cookie) = cookie::Cookie::new_wrapped(bare_cookie, &request, source) {
let cookie_jar = &self.cookie_jar; let mut cookie_jar = resource_group.cookie_jar.write().unwrap();
let mut cookie_jar = cookie_jar.write().unwrap();
cookie_jar.push(cookie, source); cookie_jar.push(cookie, source);
} }
} }
@ -435,7 +492,8 @@ impl CoreResourceManager {
load_data: LoadData, load_data: LoadData,
consumer: LoadConsumer, consumer: LoadConsumer,
id_sender: Option<IpcSender<ResourceId>>, id_sender: Option<IpcSender<ResourceId>>,
resource_thread: CoreResourceThread) { resource_thread: CoreResourceThread,
resource_grp: &ResourceGroup) {
fn from_factory(factory: fn(LoadData, LoadConsumer, Arc<MIMEClassifier>, CancellationListener)) fn from_factory(factory: fn(LoadData, LoadConsumer, Arc<MIMEClassifier>, CancellationListener))
-> Box<FnBox(LoadData, -> Box<FnBox(LoadData,
LoadConsumer, LoadConsumer,
@ -461,16 +519,16 @@ impl CoreResourceManager {
"file" => from_factory(file_loader::factory), "file" => from_factory(file_loader::factory),
"http" | "https" | "view-source" => { "http" | "https" | "view-source" => {
let http_state = HttpState { let http_state = HttpState {
hsts_list: self.hsts_list.clone(),
cookie_jar: self.cookie_jar.clone(),
auth_cache: self.auth_cache.clone(),
blocked_content: BLOCKED_CONTENT_RULES.clone(), blocked_content: BLOCKED_CONTENT_RULES.clone(),
hsts_list: resource_grp.hsts_list.clone(),
cookie_jar: resource_grp.cookie_jar.clone(),
auth_cache: resource_grp.auth_cache.clone()
}; };
http_loader::factory(self.user_agent.clone(), http_loader::factory(self.user_agent.clone(),
http_state, http_state,
self.devtools_chan.clone(), self.devtools_chan.clone(),
self.profiler_chan.clone(), self.profiler_chan.clone(),
self.connector.clone()) resource_grp.connector.clone())
}, },
"data" => from_factory(data_loader::factory), "data" => from_factory(data_loader::factory),
"about" => from_factory(about_loader::factory), "about" => from_factory(about_loader::factory),
@ -488,11 +546,14 @@ impl CoreResourceManager {
cancel_listener)); cancel_listener));
} }
fn fetch(&self, init: RequestInit, sender: IpcSender<FetchResponseMsg>) { fn fetch(&self,
init: RequestInit,
sender: IpcSender<FetchResponseMsg>,
group: &ResourceGroup) {
let http_state = HttpState { let http_state = HttpState {
hsts_list: self.hsts_list.clone(), hsts_list: group.hsts_list.clone(),
cookie_jar: self.cookie_jar.clone(), cookie_jar: group.cookie_jar.clone(),
auth_cache: self.auth_cache.clone(), auth_cache: group.auth_cache.clone(),
blocked_content: BLOCKED_CONTENT_RULES.clone(), blocked_content: BLOCKED_CONTENT_RULES.clone(),
}; };
let ua = self.user_agent.clone(); let ua = self.user_agent.clone();
@ -510,7 +571,8 @@ impl CoreResourceManager {
fn websocket_connect(&self, fn websocket_connect(&self,
connect: WebSocketCommunicate, connect: WebSocketCommunicate,
connect_data: WebSocketConnectData) { connect_data: WebSocketConnectData,
websocket_loader::init(connect, connect_data, self.cookie_jar.clone()); resource_grp: &ResourceGroup) {
websocket_loader::init(connect, connect_data, resource_grp.cookie_jar.clone());
} }
} }

View file

@ -39,7 +39,6 @@ use msg::constellation_msg::{PipelineId, ReferrerPolicy};
use request::{Request, RequestInit}; use request::{Request, RequestInit};
use response::{HttpsState, Response}; use response::{HttpsState, Response};
use std::io::Error as IOError; use std::io::Error as IOError;
use std::sync::mpsc::Sender;
use std::thread; use std::thread;
use storage_thread::StorageThreadMsg; use storage_thread::StorageThreadMsg;
use url::Url; use url::Url;
@ -649,9 +648,10 @@ pub fn unwrap_websocket_protocol(wsp: Option<&header::WebSocketProtocol>) -> Opt
#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] #[derive(Clone, PartialEq, Eq, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)]
pub struct ResourceId(pub u32); pub struct ResourceId(pub u32);
#[derive(Deserialize, Serialize)]
pub enum ConstellationMsg { pub enum ConstellationMsg {
/// Queries whether a pipeline or its ancestors are private /// Queries whether a pipeline or its ancestors are private
IsPrivate(PipelineId, Sender<bool>), IsPrivate(PipelineId, IpcSender<bool>),
} }
/// Network errors that have to be exported out of the loaders /// Network errors that have to be exported out of the loaders

View file

@ -562,6 +562,21 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement {
make_getter!(Height, "height"); make_getter!(Height, "height");
// https://html.spec.whatwg.org/multipage/#dom-dim-height // https://html.spec.whatwg.org/multipage/#dom-dim-height
make_dimension_setter!(SetHeight, "height"); make_dimension_setter!(SetHeight, "height");
// check-tidy: no specs after this line
fn SetMozprivatebrowsing(&self, value: bool) {
let element = self.upcast::<Element>();
element.set_bool_attribute(&Atom::from("mozprivatebrowsing"), value);
}
fn Mozprivatebrowsing(&self) -> bool {
if window_from_node(self).is_mozbrowser() {
let element = self.upcast::<Element>();
element.has_attribute(&Atom::from("mozprivatebrowsing"))
} else {
false
}
}
} }
impl VirtualMethods for HTMLIFrameElement { impl VirtualMethods for HTMLIFrameElement {

View file

@ -33,6 +33,9 @@ partial interface HTMLIFrameElement {
partial interface HTMLIFrameElement { partial interface HTMLIFrameElement {
[Func="::dom::window::Window::global_is_mozbrowser"] [Func="::dom::window::Window::global_is_mozbrowser"]
attribute boolean mozbrowser; attribute boolean mozbrowser;
[Func="::dom::window::Window::global_is_mozbrowser"]
attribute boolean mozprivatebrowsing;
}; };
HTMLIFrameElement implements BrowserElement; HTMLIFrameElement implements BrowserElement;

View file

@ -208,12 +208,13 @@ fn create_constellation(opts: opts::Opts,
webrender_api_sender: Option<webrender_traits::RenderApiSender>) -> Sender<ConstellationMsg> { webrender_api_sender: Option<webrender_traits::RenderApiSender>) -> Sender<ConstellationMsg> {
let bluetooth_thread: IpcSender<BluetoothMethodMsg> = BluetoothThreadFactory::new(); let bluetooth_thread: IpcSender<BluetoothMethodMsg> = BluetoothThreadFactory::new();
let resource_threads = new_resource_threads(opts.user_agent.clone(), let (public_resource_threads, private_resource_threads) =
new_resource_threads(opts.user_agent.clone(),
devtools_chan.clone(), devtools_chan.clone(),
time_profiler_chan.clone()); time_profiler_chan.clone());
let image_cache_thread = new_image_cache_thread(resource_threads.sender(), let image_cache_thread = new_image_cache_thread(public_resource_threads.sender(),
webrender_api_sender.as_ref().map(|wr| wr.create_api())); webrender_api_sender.as_ref().map(|wr| wr.create_api()));
let font_cache_thread = FontCacheThread::new(resource_threads.sender(), let font_cache_thread = FontCacheThread::new(public_resource_threads.sender(),
webrender_api_sender.as_ref().map(|wr| wr.create_api())); webrender_api_sender.as_ref().map(|wr| wr.create_api()));
let initial_state = InitialConstellationState { let initial_state = InitialConstellationState {
@ -222,7 +223,8 @@ fn create_constellation(opts: opts::Opts,
bluetooth_thread: bluetooth_thread, bluetooth_thread: bluetooth_thread,
image_cache_thread: image_cache_thread, image_cache_thread: image_cache_thread,
font_cache_thread: font_cache_thread, font_cache_thread: font_cache_thread,
resource_threads: resource_threads, public_resource_threads: public_resource_threads,
private_resource_threads: private_resource_threads,
time_profiler_chan: time_profiler_chan, time_profiler_chan: time_profiler_chan,
mem_profiler_chan: mem_profiler_chan, mem_profiler_chan: mem_profiler_chan,
supports_clipboard: supports_clipboard, supports_clipboard: supports_clipboard,

View file

@ -40,7 +40,7 @@ impl LoadOrigin for ResourceTest {
fn test_exit() { fn test_exit() {
let (tx, _rx) = ipc::channel().unwrap(); let (tx, _rx) = ipc::channel().unwrap();
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
let resource_thread = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx)); let (resource_thread, _) = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx));
resource_thread.send(CoreResourceMsg::Exit(sender)).unwrap(); resource_thread.send(CoreResourceMsg::Exit(sender)).unwrap();
receiver.recv().unwrap(); receiver.recv().unwrap();
} }
@ -49,7 +49,7 @@ fn test_exit() {
fn test_bad_scheme() { fn test_bad_scheme() {
let (tx, _rx) = ipc::channel().unwrap(); let (tx, _rx) = ipc::channel().unwrap();
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
let resource_thread = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx)); let (resource_thread, _) = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx));
let (start_chan, start) = ipc::channel().unwrap(); let (start_chan, start) = ipc::channel().unwrap();
let url = Url::parse("bogus://whatever").unwrap(); let url = Url::parse("bogus://whatever").unwrap();
resource_thread.send(CoreResourceMsg::Load(LoadData::new(LoadContext::Browsing, url, &ResourceTest), resource_thread.send(CoreResourceMsg::Load(LoadData::new(LoadContext::Browsing, url, &ResourceTest),
@ -228,7 +228,7 @@ fn test_cancelled_listener() {
let (tx, _rx) = ipc::channel().unwrap(); let (tx, _rx) = ipc::channel().unwrap();
let (exit_sender, exit_receiver) = ipc::channel().unwrap(); let (exit_sender, exit_receiver) = ipc::channel().unwrap();
let resource_thread = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx)); let (resource_thread, _) = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx));
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
let (id_sender, id_receiver) = ipc::channel().unwrap(); let (id_sender, id_receiver) = ipc::channel().unwrap();
let (sync_sender, sync_receiver) = ipc::channel().unwrap(); let (sync_sender, sync_receiver) = ipc::channel().unwrap();

View file

@ -6616,6 +6616,12 @@
"url": "/_mozilla/mozilla/mozbrowser/mozbrowsertitlechangedeagerly_event.html" "url": "/_mozilla/mozilla/mozbrowser/mozbrowsertitlechangedeagerly_event.html"
} }
], ],
"mozilla/mozbrowser/private_browsing.html": [
{
"path": "mozilla/mozbrowser/private_browsing.html",
"url": "/_mozilla/mozilla/mozbrowser/private_browsing.html"
}
],
"mozilla/mozbrowser/redirect.html": [ "mozilla/mozbrowser/redirect.html": [
{ {
"path": "mozilla/mozbrowser/redirect.html", "path": "mozilla/mozbrowser/redirect.html",

View file

@ -0,0 +1,3 @@
<html><body><div id="test">Normal iFrame</div></body>
<script>alert(document.cookie)</script>
</html>

View file

@ -0,0 +1,4 @@
<span id="cookie"></span>
<script>
document.querySelector('#cookie').innerHTML = document.cookie;
</script>

View file

@ -0,0 +1,13 @@
<html>
<body>
<div id="test">Private iFrame</div>
</body>
<script>document.cookie = "private=active;path=/";</script>
<iframe src="iframe_privateContent_grandchild.html"></iframe>
<script>
var iframe = document.querySelector('iframe');
iframe.onload = function() {
alert(document.cookie + ' ' + iframe.contentDocument.querySelector('#cookie').textContent);
};
</script>
</html>

View file

@ -0,0 +1,44 @@
<head>
<meta charset="utf8" />
<title>Private browsing</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
async_test(function(t) {
var privateFrame = document.createElement("iframe");
privateFrame.mozbrowser = true;
privateFrame.mozprivatebrowsing = true;
var gotGrandchildResult = false;
privateFrame.addEventListener("mozbrowsershowmodalprompt", t.step_func(e => {
assert_equals(e.detail.message, 'private=active private=active');
gotGrandchildResult = true;
}));
privateFrame.onload = t.step_func(function() {
assert_true(gotGrandchildResult);
var parent = privateFrame.parentNode;
parent.removeChild(privateFrame);
var iframe = document.createElement("iframe");
var promptDisplay = false;
iframe.mozbrowser = true;
iframe.onload = t.step_func(function() {
assert_true(promptDisplay);
t.done();
});
iframe.addEventListener("mozbrowsershowmodalprompt", t.step_func(e => {
promptDisplay = true;
assert_equals(e.detail.message, "");
}));
iframe.src = "iframe_contentDocument_inner.html";
parent.appendChild(iframe);
});
privateFrame.src = "iframe_privateContent_inner.html";
document.body.appendChild(privateFrame);
});
</script>
</body>