mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
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:
parent
a5778fb5da
commit
7e2b4d163b
14 changed files with 294 additions and 138 deletions
|
@ -108,7 +108,10 @@ pub struct Constellation<Message, LTF, STF> {
|
|||
compositor_proxy: Box<CompositorProxy>,
|
||||
|
||||
/// 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.
|
||||
image_cache_thread: ImageCacheThread,
|
||||
|
@ -201,7 +204,9 @@ pub struct InitialConstellationState {
|
|||
/// A channel to the font cache thread.
|
||||
pub font_cache_thread: FontCacheThread,
|
||||
/// 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.
|
||||
pub time_profiler_chan: time::ProfilerChan,
|
||||
/// A channel to the memory profiler thread.
|
||||
|
@ -331,7 +336,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
compositor_proxy: state.compositor_proxy,
|
||||
devtools_chan: state.devtools_chan,
|
||||
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,
|
||||
font_cache_thread: state.font_cache_thread,
|
||||
pipelines: HashMap::new(),
|
||||
|
@ -404,9 +410,16 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
parent_info: Option<(PipelineId, SubpageId, FrameType)>,
|
||||
initial_window_size: Option<TypedSize2D<PagePx, f32>>,
|
||||
script_channel: Option<IpcSender<ConstellationControlMsg>>,
|
||||
load_data: LoadData) {
|
||||
load_data: LoadData,
|
||||
is_private: bool) {
|
||||
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 {
|
||||
self.pipelines.get(&parent_pipeline_id).map(|pipeline| pipeline.visible)
|
||||
} else {
|
||||
|
@ -425,7 +438,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
bluetooth_thread: self.bluetooth_thread.clone(),
|
||||
image_cache_thread: self.image_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(),
|
||||
mem_profiler_chan: self.mem_profiler_chan.clone(),
|
||||
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(),
|
||||
parent_visibility: parent_visibility,
|
||||
webrender_api_sender: self.webrender_api_sender.clone(),
|
||||
is_private: is_private,
|
||||
});
|
||||
|
||||
let (pipeline, child_process) = match result {
|
||||
|
@ -850,7 +864,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
self.image_cache_thread.exit();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -863,12 +877,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -951,7 +965,7 @@ 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, 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));
|
||||
|
||||
|
@ -965,7 +979,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
let root_pipeline_id = PipelineId::new();
|
||||
debug_assert!(PipelineId::fake_root_pipeline_id() == root_pipeline_id);
|
||||
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.push_pending_frame(root_pipeline_id, None);
|
||||
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)))
|
||||
.cloned();
|
||||
|
||||
let (load_data, script_chan, window_size) = {
|
||||
let (load_data, script_chan, window_size, is_private) = {
|
||||
let old_pipeline = 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
|
||||
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;
|
||||
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 {
|
||||
|
@ -1077,16 +1094,17 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
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
|
||||
self.new_pipeline(load_info.new_pipeline_id,
|
||||
Some((load_info.containing_pipeline_id, load_info.new_subpage_id, load_info.frame_type)),
|
||||
window_size,
|
||||
script_chan,
|
||||
load_data);
|
||||
load_data,
|
||||
is_private);
|
||||
|
||||
self.subpage_map.insert((load_info.containing_pipeline_id, load_info.new_subpage_id),
|
||||
load_info.new_pipeline_id);
|
||||
|
@ -1217,7 +1235,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
// Create the new pipeline
|
||||
let window_size = self.pipelines.get(&source_id).and_then(|source| source.size);
|
||||
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));
|
||||
|
||||
// Send message to ScriptThread that will suspend all timers
|
||||
|
@ -1906,25 +1924,6 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
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)
|
||||
fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) {
|
||||
// Store information about the pipelines to be closed. Then close the
|
||||
|
|
|
@ -64,6 +64,7 @@ pub struct Pipeline {
|
|||
/// animations cause composites to be continually scheduled.
|
||||
pub running_animations: bool,
|
||||
pub children: Vec<FrameId>,
|
||||
/// Whether this pipeline is considered distinct from public pipelines.
|
||||
pub is_private: bool,
|
||||
/// Whether this pipeline should be treated as visible for the purposes of scheduling and
|
||||
/// resource management.
|
||||
|
@ -119,6 +120,8 @@ pub struct InitialPipelineState {
|
|||
pub parent_visibility: Option<bool>,
|
||||
/// Optional webrender api (if enabled).
|
||||
pub webrender_api_sender: Option<webrender_traits::RenderApiSender>,
|
||||
/// Whether this pipeline is considered private.
|
||||
pub is_private: bool,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
|
@ -254,6 +257,7 @@ impl Pipeline {
|
|||
pipeline_chan,
|
||||
state.compositor_proxy,
|
||||
chrome_to_paint_chan,
|
||||
state.is_private,
|
||||
state.load_data.url,
|
||||
state.window_size,
|
||||
state.parent_visibility.unwrap_or(true));
|
||||
|
@ -269,6 +273,7 @@ impl Pipeline {
|
|||
layout_chan: IpcSender<LayoutControlMsg>,
|
||||
compositor_proxy: Box<CompositorProxy + 'static + Send>,
|
||||
chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
|
||||
is_private: bool,
|
||||
url: Url,
|
||||
size: Option<TypedSize2D<PagePx, f32>>,
|
||||
visible: bool)
|
||||
|
@ -285,8 +290,8 @@ impl Pipeline {
|
|||
children: vec!(),
|
||||
size: size,
|
||||
running_animations: false,
|
||||
is_private: false,
|
||||
visible: visible,
|
||||
is_private: is_private,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,11 +19,13 @@ use http_loader::{self, HttpState};
|
|||
use hyper::client::pool::Pool;
|
||||
use hyper::header::{ContentType, Header, SetCookie};
|
||||
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 net_traits::LoadContext;
|
||||
use net_traits::ProgressMsg::Done;
|
||||
use net_traits::filemanager_thread::FileManagerThreadMsg;
|
||||
use net_traits::request::{Request, RequestInit};
|
||||
use net_traits::storage_thread::StorageThreadMsg;
|
||||
use net_traits::{AsyncResponseTarget, Metadata, ProgressMsg, ResponseAction, CoreResourceThread};
|
||||
use net_traits::{CoreResourceMsg, CookieSource, FetchResponseMsg, FetchTaskTarget, LoadConsumer};
|
||||
use net_traits::{LoadData, LoadResponse, NetworkError, ResourceId};
|
||||
|
@ -56,6 +58,14 @@ pub enum ProgressSender {
|
|||
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 {
|
||||
//XXXjdm return actual error
|
||||
pub fn send(&self, msg: ProgressMsg) -> Result<(), ()> {
|
||||
|
@ -158,89 +168,149 @@ 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,
|
||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||
profiler_chan: ProfilerChan) -> ResourceThreads {
|
||||
ResourceThreads::new(new_core_resource_thread(user_agent, devtools_chan, profiler_chan),
|
||||
StorageThreadFactory::new(),
|
||||
FileManagerThreadFactory::new(TFD_PROVIDER))
|
||||
profiler_chan: ProfilerChan) -> (ResourceThreads, ResourceThreads) {
|
||||
let (public_core, private_core) = new_core_resource_thread(user_agent, devtools_chan, profiler_chan);
|
||||
let storage: IpcSender<StorageThreadMsg> = StorageThreadFactory::new();
|
||||
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
|
||||
pub fn new_core_resource_thread(user_agent: String,
|
||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||
profiler_chan: ProfilerChan) -> CoreResourceThread {
|
||||
let hsts_preload = HstsList::from_servo_preload();
|
||||
let (setup_chan, setup_port) = ipc::channel().unwrap();
|
||||
let setup_chan_clone = setup_chan.clone();
|
||||
profiler_chan: ProfilerChan)
|
||||
-> (CoreResourceThread, CoreResourceThread) {
|
||||
let (public_setup_chan, public_setup_port) = ipc::channel().unwrap();
|
||||
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 || {
|
||||
let resource_manager = CoreResourceManager::new(
|
||||
user_agent, hsts_preload, devtools_chan, profiler_chan
|
||||
user_agent, devtools_chan, profiler_chan
|
||||
);
|
||||
|
||||
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 {
|
||||
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 {
|
||||
fn start(&mut self, control_sender: CoreResourceThread) {
|
||||
loop {
|
||||
match self.from_client.recv().unwrap() {
|
||||
CoreResourceMsg::Load(load_data, consumer, id_sender) =>
|
||||
self.resource_manager.load(load_data, consumer, id_sender, control_sender.clone()),
|
||||
CoreResourceMsg::Fetch(init, sender) =>
|
||||
self.resource_manager.fetch(init, sender),
|
||||
CoreResourceMsg::WebsocketConnect(connect, connect_data) =>
|
||||
self.resource_manager.websocket_connect(connect, connect_data),
|
||||
CoreResourceMsg::SetCookiesForUrl(request, cookie_list, source) =>
|
||||
self.resource_manager.set_cookies_for_url(request, cookie_list, source),
|
||||
CoreResourceMsg::GetCookiesForUrl(url, consumer, source) => {
|
||||
let cookie_jar = &self.resource_manager.cookie_jar;
|
||||
let mut cookie_jar = cookie_jar.write().unwrap();
|
||||
consumer.send(cookie_jar.cookies_for_url(&url, source)).unwrap();
|
||||
}
|
||||
CoreResourceMsg::Cancel(res_id) => {
|
||||
if let Some(cancel_sender) = self.resource_manager.cancel_load_map.get(&res_id) {
|
||||
let _ = cancel_sender.send(());
|
||||
}
|
||||
self.resource_manager.cancel_load_map.remove(&res_id);
|
||||
}
|
||||
CoreResourceMsg::Synchronize(sender) => {
|
||||
let _ = sender.send(());
|
||||
}
|
||||
CoreResourceMsg::Exit(sender) => {
|
||||
if let Some(ref config_dir) = opts::get().config_dir {
|
||||
match self.resource_manager.auth_cache.read() {
|
||||
Ok(auth_cache) => write_json_to_file(&*auth_cache, config_dir, "auth_cache.json"),
|
||||
Err(_) => warn!("Error writing auth cache to disk"),
|
||||
}
|
||||
match self.resource_manager.cookie_jar.read() {
|
||||
Ok(jar) => write_json_to_file(&*jar, config_dir, "cookie_jar.json"),
|
||||
Err(_) => warn!("Error writing cookie jar to disk"),
|
||||
}
|
||||
match self.resource_manager.hsts_list.read() {
|
||||
Ok(hsts) => write_json_to_file(&*hsts, config_dir, "hsts_list.json"),
|
||||
Err(_) => warn!("Error writing hsts list to disk"),
|
||||
}
|
||||
}
|
||||
let _ = sender.send(());
|
||||
break;
|
||||
}
|
||||
#[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 {
|
||||
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) =>
|
||||
self.resource_manager.load(load_data, consumer, id_sender, control_sender.clone(), group),
|
||||
CoreResourceMsg::Fetch(init, sender) =>
|
||||
self.resource_manager.fetch(init, sender, group),
|
||||
CoreResourceMsg::WebsocketConnect(connect, connect_data) =>
|
||||
self.resource_manager.websocket_connect(connect, connect_data, group),
|
||||
CoreResourceMsg::SetCookiesForUrl(request, cookie_list, source) =>
|
||||
self.resource_manager.set_cookies_for_url(request, cookie_list, source, group),
|
||||
CoreResourceMsg::GetCookiesForUrl(url, consumer, source) => {
|
||||
let mut cookie_jar = group.cookie_jar.write().unwrap();
|
||||
consumer.send(cookie_jar.cookies_for_url(&url, source)).unwrap();
|
||||
}
|
||||
CoreResourceMsg::Cancel(res_id) => {
|
||||
if let Some(cancel_sender) = self.resource_manager.cancel_load_map.get(&res_id) {
|
||||
let _ = cancel_sender.send(());
|
||||
}
|
||||
self.resource_manager.cancel_load_map.remove(&res_id);
|
||||
}
|
||||
CoreResourceMsg::Synchronize(sender) => {
|
||||
let _ = sender.send(());
|
||||
}
|
||||
CoreResourceMsg::Exit(sender) => {
|
||||
if let Some(ref config_dir) = opts::get().config_dir {
|
||||
match group.auth_cache.read() {
|
||||
Ok(auth_cache) => write_json_to_file(&*auth_cache, config_dir, "auth_cache.json"),
|
||||
Err(_) => warn!("Error writing auth cache to disk"),
|
||||
}
|
||||
match group.cookie_jar.read() {
|
||||
Ok(jar) => write_json_to_file(&*jar, config_dir, "cookie_jar.json"),
|
||||
Err(_) => warn!("Error writing cookie jar to disk"),
|
||||
}
|
||||
match group.hsts_list.read() {
|
||||
Ok(hsts) => write_json_to_file(&*hsts, config_dir, "hsts_list.json"),
|
||||
Err(_) => warn!("Error writing hsts list to disk"),
|
||||
}
|
||||
}
|
||||
let _ = sender.send(());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_json_from_file<T: Decodable>(data: &mut T, config_dir: &str, filename: &str) {
|
||||
|
@ -381,50 +451,37 @@ pub struct AuthCache {
|
|||
|
||||
pub struct CoreResourceManager {
|
||||
user_agent: String,
|
||||
cookie_jar: Arc<RwLock<CookieStorage>>,
|
||||
auth_cache: Arc<RwLock<AuthCache>>,
|
||||
mime_classifier: Arc<MIMEClassifier>,
|
||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||
profiler_chan: ProfilerChan,
|
||||
hsts_list: Arc<RwLock<HstsList>>,
|
||||
connector: Arc<Pool<Connector>>,
|
||||
cancel_load_map: HashMap<ResourceId, Sender<()>>,
|
||||
next_resource_id: ResourceId,
|
||||
}
|
||||
|
||||
impl CoreResourceManager {
|
||||
pub fn new(user_agent: String,
|
||||
mut hsts_list: HstsList,
|
||||
devtools_channel: Option<Sender<DevtoolsControlMsg>>,
|
||||
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 {
|
||||
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()),
|
||||
devtools_chan: devtools_channel,
|
||||
profiler_chan: profiler_chan,
|
||||
hsts_list: Arc::new(RwLock::new(hsts_list)),
|
||||
connector: create_http_connector(),
|
||||
cancel_load_map: HashMap::new(),
|
||||
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()]);
|
||||
if let Ok(SetCookie(cookies)) = header {
|
||||
for bare_cookie in cookies {
|
||||
if let Some(cookie) = cookie::Cookie::new_wrapped(bare_cookie, &request, source) {
|
||||
let cookie_jar = &self.cookie_jar;
|
||||
let mut cookie_jar = cookie_jar.write().unwrap();
|
||||
let mut cookie_jar = resource_group.cookie_jar.write().unwrap();
|
||||
cookie_jar.push(cookie, source);
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +492,8 @@ impl CoreResourceManager {
|
|||
load_data: LoadData,
|
||||
consumer: LoadConsumer,
|
||||
id_sender: Option<IpcSender<ResourceId>>,
|
||||
resource_thread: CoreResourceThread) {
|
||||
resource_thread: CoreResourceThread,
|
||||
resource_grp: &ResourceGroup) {
|
||||
fn from_factory(factory: fn(LoadData, LoadConsumer, Arc<MIMEClassifier>, CancellationListener))
|
||||
-> Box<FnBox(LoadData,
|
||||
LoadConsumer,
|
||||
|
@ -461,16 +519,16 @@ impl CoreResourceManager {
|
|||
"file" => from_factory(file_loader::factory),
|
||||
"http" | "https" | "view-source" => {
|
||||
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(),
|
||||
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_state,
|
||||
self.devtools_chan.clone(),
|
||||
self.profiler_chan.clone(),
|
||||
self.connector.clone())
|
||||
resource_grp.connector.clone())
|
||||
},
|
||||
"data" => from_factory(data_loader::factory),
|
||||
"about" => from_factory(about_loader::factory),
|
||||
|
@ -488,11 +546,14 @@ impl CoreResourceManager {
|
|||
cancel_listener));
|
||||
}
|
||||
|
||||
fn fetch(&self, init: RequestInit, sender: IpcSender<FetchResponseMsg>) {
|
||||
fn fetch(&self,
|
||||
init: RequestInit,
|
||||
sender: IpcSender<FetchResponseMsg>,
|
||||
group: &ResourceGroup) {
|
||||
let http_state = HttpState {
|
||||
hsts_list: self.hsts_list.clone(),
|
||||
cookie_jar: self.cookie_jar.clone(),
|
||||
auth_cache: self.auth_cache.clone(),
|
||||
hsts_list: group.hsts_list.clone(),
|
||||
cookie_jar: group.cookie_jar.clone(),
|
||||
auth_cache: group.auth_cache.clone(),
|
||||
blocked_content: BLOCKED_CONTENT_RULES.clone(),
|
||||
};
|
||||
let ua = self.user_agent.clone();
|
||||
|
@ -510,7 +571,8 @@ impl CoreResourceManager {
|
|||
|
||||
fn websocket_connect(&self,
|
||||
connect: WebSocketCommunicate,
|
||||
connect_data: WebSocketConnectData) {
|
||||
websocket_loader::init(connect, connect_data, self.cookie_jar.clone());
|
||||
connect_data: WebSocketConnectData,
|
||||
resource_grp: &ResourceGroup) {
|
||||
websocket_loader::init(connect, connect_data, resource_grp.cookie_jar.clone());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ use msg::constellation_msg::{PipelineId, ReferrerPolicy};
|
|||
use request::{Request, RequestInit};
|
||||
use response::{HttpsState, Response};
|
||||
use std::io::Error as IOError;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::thread;
|
||||
use storage_thread::StorageThreadMsg;
|
||||
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)]
|
||||
pub struct ResourceId(pub u32);
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum ConstellationMsg {
|
||||
/// 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
|
||||
|
|
|
@ -562,6 +562,21 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement {
|
|||
make_getter!(Height, "height");
|
||||
// https://html.spec.whatwg.org/multipage/#dom-dim-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 {
|
||||
|
|
|
@ -33,6 +33,9 @@ partial interface HTMLIFrameElement {
|
|||
partial interface HTMLIFrameElement {
|
||||
[Func="::dom::window::Window::global_is_mozbrowser"]
|
||||
attribute boolean mozbrowser;
|
||||
|
||||
[Func="::dom::window::Window::global_is_mozbrowser"]
|
||||
attribute boolean mozprivatebrowsing;
|
||||
};
|
||||
|
||||
HTMLIFrameElement implements BrowserElement;
|
||||
|
|
|
@ -489,8 +489,8 @@ impl Runnable for ConnectionEstablishedTask {
|
|||
for cookie in cookies.iter() {
|
||||
if let Ok(cookie_value) = String::from_utf8(cookie.clone()) {
|
||||
let _ = ws.global().r().core_resource_thread().send(SetCookiesForUrl(ws.url.clone(),
|
||||
cookie_value,
|
||||
HTTP));
|
||||
cookie_value,
|
||||
HTTP));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -208,12 +208,13 @@ fn create_constellation(opts: opts::Opts,
|
|||
webrender_api_sender: Option<webrender_traits::RenderApiSender>) -> Sender<ConstellationMsg> {
|
||||
let bluetooth_thread: IpcSender<BluetoothMethodMsg> = BluetoothThreadFactory::new();
|
||||
|
||||
let resource_threads = new_resource_threads(opts.user_agent.clone(),
|
||||
devtools_chan.clone(),
|
||||
time_profiler_chan.clone());
|
||||
let image_cache_thread = new_image_cache_thread(resource_threads.sender(),
|
||||
let (public_resource_threads, private_resource_threads) =
|
||||
new_resource_threads(opts.user_agent.clone(),
|
||||
devtools_chan.clone(),
|
||||
time_profiler_chan.clone());
|
||||
let image_cache_thread = new_image_cache_thread(public_resource_threads.sender(),
|
||||
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()));
|
||||
|
||||
let initial_state = InitialConstellationState {
|
||||
|
@ -222,7 +223,8 @@ fn create_constellation(opts: opts::Opts,
|
|||
bluetooth_thread: bluetooth_thread,
|
||||
image_cache_thread: image_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,
|
||||
mem_profiler_chan: mem_profiler_chan,
|
||||
supports_clipboard: supports_clipboard,
|
||||
|
|
|
@ -40,7 +40,7 @@ impl LoadOrigin for ResourceTest {
|
|||
fn test_exit() {
|
||||
let (tx, _rx) = 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();
|
||||
receiver.recv().unwrap();
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ fn test_exit() {
|
|||
fn test_bad_scheme() {
|
||||
let (tx, _rx) = 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 url = Url::parse("bogus://whatever").unwrap();
|
||||
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 (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 (id_sender, id_receiver) = ipc::channel().unwrap();
|
||||
let (sync_sender, sync_receiver) = ipc::channel().unwrap();
|
||||
|
|
|
@ -6616,6 +6616,12 @@
|
|||
"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": [
|
||||
{
|
||||
"path": "mozilla/mozbrowser/redirect.html",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<html><body><div id="test">Normal iFrame</div></body>
|
||||
<script>alert(document.cookie)</script>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
<span id="cookie"></span>
|
||||
<script>
|
||||
document.querySelector('#cookie').innerHTML = document.cookie;
|
||||
</script>
|
|
@ -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>
|
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue