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>,
/// 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

View file

@ -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,
}
}

View file

@ -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());
}
}

View file

@ -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

View file

@ -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 {

View file

@ -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;

View file

@ -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));
}
}
}

View file

@ -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,

View file

@ -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();

View file

@ -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",

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>