diff --git a/Cargo.lock b/Cargo.lock index 7169889923a..0f9a5a4dba8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1885,6 +1885,7 @@ version = "0.0.1" dependencies = [ "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "brotli 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "compositing 0.0.1", "cookie 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "devtools_traits 0.0.1", "embedder_traits 0.0.1", @@ -1914,7 +1915,6 @@ dependencies = [ "servo_url 0.0.1", "threadpool 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "tinyfiledialogs 3.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs index 0b042ec5350..2e8dc7a66d6 100644 --- a/components/compositing/compositor_thread.rs +++ b/components/compositing/compositor_thread.rs @@ -9,6 +9,7 @@ use compositor::CompositingReason; use gfx_traits::Epoch; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::{InputMethodType, Key, KeyModifiers, KeyState, PipelineId, TopLevelBrowsingContextId}; +use net_traits::filemanager_thread::FilterPattern; use net_traits::image::base::Image; use profile_traits::mem; use profile_traits::time; @@ -143,6 +144,8 @@ pub enum EmbedderMsg { Panic(TopLevelBrowsingContextId, String, Option), /// Open dialog to select bluetooth device. GetSelectedBluetoothDevice(Vec, IpcSender>), + /// Open file dialog to select files. Set boolean flag to true allows to select multiple files. + SelectFiles(Vec, bool, IpcSender>>), /// Request to present an IME to the user when an editable element is focused. ShowIME(TopLevelBrowsingContextId, InputMethodType), /// Request to hide the IME when the editable element is blurred. @@ -248,6 +251,7 @@ impl Debug for EmbedderMsg { EmbedderMsg::LoadComplete(..) => write!(f, "LoadComplete"), EmbedderMsg::Panic(..) => write!(f, "Panic"), EmbedderMsg::GetSelectedBluetoothDevice(..) => write!(f, "GetSelectedBluetoothDevice"), + EmbedderMsg::SelectFiles(..) => write!(f, "SelectFiles"), EmbedderMsg::ShowIME(..) => write!(f, "ShowIME"), EmbedderMsg::HideIME(..) => write!(f, "HideIME"), EmbedderMsg::Shutdown => write!(f, "Shutdown"), diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml index 4fdee627ca9..4aaeecf75d9 100644 --- a/components/net/Cargo.toml +++ b/components/net/Cargo.toml @@ -15,6 +15,7 @@ doctest = false base64 = "0.6" brotli = "1.0.6" cookie = "0.10" +compositing = {path = "../compositing"} devtools_traits = {path = "../devtools_traits"} embedder_traits = { path = "../embedder_traits" } flate2 = "1" @@ -48,9 +49,6 @@ url = "1.2" uuid = {version = "0.6", features = ["v4"]} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} -[target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies] -tinyfiledialogs = "3.0" - [[test]] name = "main" path = "tests/main.rs" diff --git a/components/net/filemanager_thread.rs b/components/net/filemanager_thread.rs index 4a84cd29714..6926412d104 100644 --- a/components/net/filemanager_thread.rs +++ b/components/net/filemanager_thread.rs @@ -2,13 +2,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use ipc_channel::ipc::IpcSender; +use compositing::compositor_thread::{EmbedderMsg, EmbedderProxy}; +use ipc_channel::ipc::{self, IpcSender}; use mime_guess::guess_mime_type_opt; use net_traits::blob_url_store::{BlobBuf, BlobURLStoreError}; use net_traits::filemanager_thread::{FileManagerResult, FileManagerThreadMsg, FileOrigin, FilterPattern}; use net_traits::filemanager_thread::{FileManagerThreadError, ReadFileProgress, RelativePos, SelectedFile}; -#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] -use servo_config::opts; use servo_config::prefs::PREFS; use std::collections::HashMap; use std::fs::File; @@ -18,72 +17,9 @@ use std::path::{Path, PathBuf}; use std::sync::{Arc, RwLock}; use std::sync::atomic::{self, AtomicBool, AtomicUsize, Ordering}; use std::thread; -#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] -use tinyfiledialogs; use url::Url; use uuid::Uuid; -/// The provider of file-dialog UI should implement this trait. -/// It will be used to initialize a generic FileManager. -/// For example, we can choose a dummy UI for testing purpose. -pub trait UIProvider where Self: Sync { - fn open_file_dialog(&self, path: &str, patterns: Vec) -> Option; - - fn open_file_dialog_multi(&self, path: &str, patterns: Vec) -> Option>; -} - -pub struct TFDProvider; - -impl UIProvider for TFDProvider { - #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] - fn open_file_dialog(&self, path: &str, patterns: Vec) -> Option { - if opts::get().headless { - return None; - } - - let mut filter = vec![]; - for p in patterns { - let s = "*.".to_string() + &p.0; - filter.push(s) - } - - let filter_ref = &(filter.iter().map(|s| s.as_str()).collect::>()[..]); - - let filter_opt = if filter.len() > 0 { Some((filter_ref, "")) } else { None }; - - tinyfiledialogs::open_file_dialog("Pick a file", path, filter_opt) - } - - #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] - fn open_file_dialog_multi(&self, path: &str, patterns: Vec) -> Option> { - if opts::get().headless { - return None; - } - - let mut filter = vec![]; - for p in patterns { - let s = "*.".to_string() + &p.0; - filter.push(s) - } - - let filter_ref = &(filter.iter().map(|s| s.as_str()).collect::>()[..]); - - let filter_opt = if filter.len() > 0 { Some((filter_ref, "")) } else { None }; - - tinyfiledialogs::open_file_dialog_multi("Pick files", path, filter_opt) - } - - #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))] - fn open_file_dialog(&self, _path: &str, _patterns: Vec) -> Option { - None - } - - #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))] - fn open_file_dialog_multi(&self, _path: &str, _patterns: Vec) -> Option> { - None - } -} - /// FileManagerStore's entry struct FileStoreEntry { /// Origin of the entry's "creator" @@ -123,12 +59,14 @@ enum FileImpl { #[derive(Clone)] pub struct FileManager { + embedder_proxy: EmbedderProxy, store: Arc, } impl FileManager { - pub fn new() -> FileManager { + pub fn new(embedder_proxy: EmbedderProxy) -> FileManager { FileManager { + embedder_proxy: embedder_proxy, store: Arc::new(FileManagerStore::new()), } } @@ -159,22 +97,20 @@ impl FileManager { } /// Message handler - pub fn handle(&self, - msg: FileManagerThreadMsg, - ui: &'static UI) - where UI: UIProvider + 'static, - { + pub fn handle(&self, msg: FileManagerThreadMsg) { match msg { FileManagerThreadMsg::SelectFile(filter, sender, origin, opt_test_path) => { let store = self.store.clone(); + let embedder = self.embedder_proxy.clone(); thread::Builder::new().name("select file".to_owned()).spawn(move || { - store.select_file(filter, sender, origin, opt_test_path, ui); + store.select_file(filter, sender, origin, opt_test_path, embedder); }).expect("Thread spawning failed"); } FileManagerThreadMsg::SelectFiles(filter, sender, origin, opt_test_paths) => { let store = self.store.clone(); + let embedder = self.embedder_proxy.clone(); thread::Builder::new().name("select files".to_owned()).spawn(move || { - store.select_files(filter, sender, origin, opt_test_paths, ui); + store.select_files(filter, sender, origin, opt_test_paths, embedder); }).expect("Thread spawning failed"); } FileManagerThreadMsg::ReadFile(sender, id, check_url_validity, origin) => { @@ -279,21 +215,36 @@ impl FileManagerStore { } } - fn select_file(&self, - patterns: Vec, - sender: IpcSender>, - origin: FileOrigin, - opt_test_path: Option, - ui: &UI) - where UI: UIProvider, - { + fn query_files_from_embedder(&self, + patterns: Vec, + multiple_files: bool, + embedder_proxy: EmbedderProxy) -> Option> { + let (ipc_sender, ipc_receiver) = ipc::channel().expect("Failed to create IPC channel!"); + let msg = EmbedderMsg::SelectFiles(patterns, multiple_files, ipc_sender); + + embedder_proxy.send(msg); + match ipc_receiver.recv() { + Ok(result) => result, + Err(e) => { + warn!("Failed to receive files from embedder ({}).", e); + None + } + } + } + + fn select_file(&self, + patterns: Vec, + sender: IpcSender>, + origin: FileOrigin, + opt_test_path: Option, + embedder_proxy: EmbedderProxy) { // Check if the select_files preference is enabled // to ensure process-level security against compromised script; // Then try applying opt_test_path directly for testing convenience let opt_s = if select_files_pref_enabled() { opt_test_path } else { - ui.open_file_dialog("", patterns) + self.query_files_from_embedder(patterns, false, embedder_proxy).and_then(|mut x| x.pop()) }; match opt_s { @@ -309,21 +260,19 @@ impl FileManagerStore { } } - fn select_files(&self, - patterns: Vec, - sender: IpcSender>>, - origin: FileOrigin, - opt_test_paths: Option>, - ui: &UI) - where UI: UIProvider, - { + fn select_files(&self, + patterns: Vec, + sender: IpcSender>>, + origin: FileOrigin, + opt_test_paths: Option>, + embedder_proxy: EmbedderProxy) { // Check if the select_files preference is enabled // to ensure process-level security against compromised script; // Then try applying opt_test_paths directly for testing convenience let opt_v = if select_files_pref_enabled() { opt_test_paths } else { - ui.open_file_dialog_multi("", patterns) + self.query_files_from_embedder(patterns, true, embedder_proxy) }; match opt_v { diff --git a/components/net/lib.rs b/components/net/lib.rs index 1bc43b361f4..a8d802ae49f 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -6,6 +6,7 @@ extern crate base64; extern crate brotli; +extern crate compositing; extern crate cookie as cookie_rs; extern crate devtools_traits; extern crate embedder_traits; @@ -36,8 +37,6 @@ extern crate servo_arc; extern crate servo_config; extern crate servo_url; extern crate time; -#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] -extern crate tinyfiledialogs; extern crate unicase; extern crate url; extern crate uuid; diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index 5e708465bf7..d00c0323f38 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! A thread that takes a URL and streams back the binary data. +use compositing::compositor_thread::EmbedderProxy; use connector::{create_http_connector, create_ssl_client}; use cookie; use cookie_rs; @@ -11,7 +12,7 @@ use devtools_traits::DevtoolsControlMsg; use embedder_traits::resources::{self, Resource}; use fetch::cors_cache::CorsCache; use fetch::methods::{CancellationListener, FetchContext, fetch}; -use filemanager_thread::{FileManager, TFDProvider}; +use filemanager_thread::FileManager; use hsts::HstsList; use http_cache::HttpCache; use http_loader::{HttpState, http_redirect_fetch}; @@ -46,13 +47,12 @@ use std::thread; use storage_thread::StorageThreadFactory; use websocket_loader; -const TFD_PROVIDER: &'static TFDProvider = &TFDProvider; - /// Returns a tuple of (public, private) senders to the new threads. pub fn new_resource_threads(user_agent: Cow<'static, str>, devtools_chan: Option>, time_profiler_chan: ProfilerChan, mem_profiler_chan: MemProfilerChan, + embedder_proxy: EmbedderProxy, config_dir: Option) -> (ResourceThreads, ResourceThreads) { let (public_core, private_core) = new_core_resource_thread( @@ -60,6 +60,7 @@ pub fn new_resource_threads(user_agent: Cow<'static, str>, devtools_chan, time_profiler_chan, mem_profiler_chan, + embedder_proxy, config_dir.clone()); let storage: IpcSender = StorageThreadFactory::new(config_dir); (ResourceThreads::new(public_core, storage.clone()), @@ -72,6 +73,7 @@ pub fn new_core_resource_thread(user_agent: Cow<'static, str>, devtools_chan: Option>, time_profiler_chan: ProfilerChan, mem_profiler_chan: MemProfilerChan, + embedder_proxy: EmbedderProxy, config_dir: Option) -> (CoreResourceThread, CoreResourceThread) { let (public_setup_chan, public_setup_port) = ipc::channel().unwrap(); @@ -80,7 +82,7 @@ pub fn new_core_resource_thread(user_agent: Cow<'static, str>, thread::Builder::new().name("ResourceManager".to_owned()).spawn(move || { let resource_manager = CoreResourceManager::new( - user_agent, devtools_chan, time_profiler_chan + user_agent, devtools_chan, time_profiler_chan, embedder_proxy ); let mut channel_manager = ResourceChannelManager { @@ -263,7 +265,7 @@ impl ResourceChannelManager { CoreResourceMsg::Synchronize(sender) => { let _ = sender.send(()); } - CoreResourceMsg::ToFileManager(msg) => self.resource_manager.filemanager.handle(msg, TFD_PROVIDER), + CoreResourceMsg::ToFileManager(msg) => self.resource_manager.filemanager.handle(msg), CoreResourceMsg::Exit(sender) => { if let Some(ref config_dir) = self.config_dir { match http_state.auth_cache.read() { @@ -374,12 +376,13 @@ pub struct CoreResourceManager { impl CoreResourceManager { pub fn new(user_agent: Cow<'static, str>, devtools_channel: Option>, - _profiler_chan: ProfilerChan) -> CoreResourceManager { + _profiler_chan: ProfilerChan, + embedder_proxy: EmbedderProxy) -> CoreResourceManager { CoreResourceManager { user_agent: user_agent, devtools_chan: devtools_channel, swmanager_chan: None, - filemanager: FileManager::new(), + filemanager: FileManager::new(embedder_proxy), } } diff --git a/components/net/tests/fetch.rs b/components/net/tests/fetch.rs index f8552b7654e..1baa2d0c275 100644 --- a/components/net/tests/fetch.rs +++ b/components/net/tests/fetch.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use {DEFAULT_USER_AGENT, new_fetch_context, fetch, make_server}; +use {DEFAULT_USER_AGENT, new_fetch_context, create_embedder_proxy, fetch, make_server}; use devtools_traits::DevtoolsControlMsg; use devtools_traits::HttpRequest as DevtoolsHttpRequest; use devtools_traits::HttpResponse as DevtoolsHttpResponse; @@ -124,7 +124,7 @@ fn test_fetch_blob() { use ipc_channel::ipc; use net_traits::blob_url_store::BlobBuf; - let context = new_fetch_context(None); + let context = new_fetch_context(None, None); let bytes = b"content"; let blob_buf = BlobBuf { @@ -553,11 +553,11 @@ fn test_fetch_with_hsts() { File::open(cert_path).unwrap().read_to_string(&mut ca_content).unwrap(); let ssl_client = create_ssl_client(&ca_content); - let context = FetchContext { + let context = FetchContext { state: Arc::new(HttpState::new(ssl_client)), user_agent: DEFAULT_USER_AGENT.into(), devtools_chan: None, - filemanager: FileManager::new(), + filemanager: FileManager::new(create_embedder_proxy()), cancellation_listener: Arc::new(Mutex::new(CancellationListener::new(None))), }; diff --git a/components/net/tests/filemanager_thread.rs b/components/net/tests/filemanager_thread.rs index 089e56c43fb..d71c9f30187 100644 --- a/components/net/tests/filemanager_thread.rs +++ b/components/net/tests/filemanager_thread.rs @@ -2,31 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use create_embedder_proxy; use ipc_channel::ipc; -use net::filemanager_thread::{FileManager, UIProvider}; +use net::filemanager_thread::FileManager; use net_traits::blob_url_store::BlobURLStoreError; use net_traits::filemanager_thread::{FilterPattern, FileManagerThreadMsg, FileManagerThreadError, ReadFileProgress}; +use servo_config::prefs::{PrefValue, PREFS}; use std::fs::File; use std::io::Read; use std::path::PathBuf; -pub const TEST_PROVIDER: &'static TestProvider = &TestProvider; - -pub struct TestProvider; - -impl UIProvider for TestProvider { - fn open_file_dialog(&self, _path: &str, _patterns: Vec) -> Option { - Some("tests/test.jpeg".to_string()) - } - - fn open_file_dialog_multi(&self, _path: &str, _patterns: Vec) -> Option> { - Some(vec!["tests/test.jpeg".to_string()]) - } -} - #[test] fn test_filemanager() { - let filemanager = FileManager::new(); + let filemanager = FileManager::new(create_embedder_proxy()); + PREFS.set("dom.testing.htmlinputelement.select_files.enabled", PrefValue::Boolean(true)); // Try to open a dummy file "components/net/tests/test.jpeg" in tree let mut handler = File::open("tests/test.jpeg").expect("test.jpeg is stolen"); @@ -41,8 +30,8 @@ fn test_filemanager() { { // Try to select a dummy file "components/net/tests/test.jpeg" let (tx, rx) = ipc::channel().unwrap(); - filemanager.handle(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone(), None), - TEST_PROVIDER); + filemanager.handle(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone(), + Some("tests/test.jpeg".to_string()))); let selected = rx.recv().expect("Broken channel") .expect("The file manager failed to find test.jpeg"); @@ -53,8 +42,7 @@ fn test_filemanager() { // Test by reading, expecting same content { let (tx2, rx2) = ipc::channel().unwrap(); - filemanager.handle(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), false, origin.clone()), - TEST_PROVIDER); + filemanager.handle(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), false, origin.clone())); let msg = rx2.recv().expect("Broken channel"); @@ -84,8 +72,7 @@ fn test_filemanager() { // Delete the id { let (tx2, rx2) = ipc::channel().unwrap(); - filemanager.handle(FileManagerThreadMsg::DecRef(selected.id.clone(), origin.clone(), tx2), - TEST_PROVIDER); + filemanager.handle(FileManagerThreadMsg::DecRef(selected.id.clone(), origin.clone(), tx2)); let ret = rx2.recv().expect("Broken channel"); assert!(ret.is_ok(), "DecRef is not okay"); @@ -94,8 +81,7 @@ fn test_filemanager() { // Test by reading again, expecting read error because we invalidated the id { let (tx2, rx2) = ipc::channel().unwrap(); - filemanager.handle(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), false, origin.clone()), - TEST_PROVIDER); + filemanager.handle(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), false, origin.clone())); let msg = rx2.recv().expect("Broken channel"); diff --git a/components/net/tests/http_loader.rs b/components/net/tests/http_loader.rs index 941dda40b20..ef5bd9a0622 100644 --- a/components/net/tests/http_loader.rs +++ b/components/net/tests/http_loader.rs @@ -506,7 +506,7 @@ fn test_load_doesnt_add_host_to_sts_list_when_url_is_http_even_if_sts_headers_ar pipeline_id: Some(TEST_PIPELINE_ID), .. RequestInit::default() }); - let context = new_fetch_context(None); + let context = new_fetch_context(None, None); let response = fetch_with_context(&mut request, &context); let _ = server.close(); @@ -523,7 +523,7 @@ fn test_load_sets_cookies_in_the_resource_manager_when_it_get_set_cookie_header_ }; let (mut server, url) = make_server(handler); - let context = new_fetch_context(None); + let context = new_fetch_context(None, None); assert_cookie_for_domain(&context.state.cookie_jar, url.as_str(), None); @@ -555,7 +555,7 @@ fn test_load_sets_requests_cookies_header_for_url_by_getting_cookies_from_the_re }; let (mut server, url) = make_server(handler); - let context = new_fetch_context(None); + let context = new_fetch_context(None, None); { let mut cookie_jar = context.state.cookie_jar.write().unwrap(); @@ -593,7 +593,7 @@ fn test_load_sends_cookie_if_nonhttp() { }; let (mut server, url) = make_server(handler); - let context = new_fetch_context(None); + let context = new_fetch_context(None, None); { let mut cookie_jar = context.state.cookie_jar.write().unwrap(); @@ -631,7 +631,7 @@ fn test_cookie_set_with_httponly_should_not_be_available_using_getcookiesforurl( }; let (mut server, url) = make_server(handler); - let context = new_fetch_context(None); + let context = new_fetch_context(None, None); assert_cookie_for_domain(&context.state.cookie_jar, url.as_str(), None); @@ -665,7 +665,7 @@ fn test_when_cookie_received_marked_secure_is_ignored_for_http() { }; let (mut server, url) = make_server(handler); - let context = new_fetch_context(None); + let context = new_fetch_context(None, None); assert_cookie_for_domain(&context.state.cookie_jar, url.as_str(), None); @@ -983,7 +983,7 @@ fn test_redirect_from_x_to_y_provides_y_cookies_from_y() { let url_y = ServoUrl::parse(&format!("http://mozilla.org:{}/org/", port)).unwrap(); *shared_url_y_clone.lock().unwrap() = Some(url_y.clone()); - let context = new_fetch_context(None); + let context = new_fetch_context(None, None); { let mut cookie_jar = context.state.cookie_jar.write().unwrap(); let cookie_x = Cookie::new_wrapped( @@ -1087,7 +1087,7 @@ fn test_if_auth_creds_not_in_url_but_in_cache_it_sets_it() { credentials_mode: CredentialsMode::Include, .. RequestInit::default() }); - let context = new_fetch_context(None); + let context = new_fetch_context(None, None); let auth_entry = AuthCacheEntry { user_name: "username".to_owned(), diff --git a/components/net/tests/main.rs b/components/net/tests/main.rs index 4e2c4c97f58..a644a6a2fd4 100644 --- a/components/net/tests/main.rs +++ b/components/net/tests/main.rs @@ -4,6 +4,7 @@ #![cfg(test)] +extern crate compositing; extern crate cookie as cookie_rs; extern crate devtools_traits; extern crate embedder_traits; @@ -16,6 +17,7 @@ extern crate msg; extern crate net; extern crate net_traits; extern crate profile_traits; +extern crate servo_config; extern crate servo_url; extern crate time; extern crate unicase; @@ -33,6 +35,7 @@ mod mime_classifier; mod resource_thread; mod subresource_integrity; +use compositing::compositor_thread::{EmbedderProxy, EventLoopWaker}; use devtools_traits::DevtoolsControlMsg; use embedder_traits::resources::{self, Resource}; use hyper::server::{Handler, Listening, Server}; @@ -54,13 +57,40 @@ struct FetchResponseCollector { sender: Sender, } -fn new_fetch_context(dc: Option>) -> FetchContext { +fn create_embedder_proxy() -> EmbedderProxy { + let (sender, _) = channel(); + let event_loop_waker = | | { + struct DummyEventLoopWaker { + } + impl DummyEventLoopWaker { + fn new() -> DummyEventLoopWaker { + DummyEventLoopWaker { } + } + } + impl EventLoopWaker for DummyEventLoopWaker { + fn wake(&self) { } + fn clone(&self) -> Box { + Box::new(DummyEventLoopWaker { }) + } + } + + Box::new(DummyEventLoopWaker::new()) + }; + + EmbedderProxy { + sender: sender, + event_loop_waker: event_loop_waker() + } +} + +fn new_fetch_context(dc: Option>, fc: Option) -> FetchContext { let ssl_client = create_ssl_client(&resources::read_string(Resource::SSLCertificates)); + let sender = fc.unwrap_or_else(|| create_embedder_proxy()); FetchContext { state: Arc::new(HttpState::new(ssl_client)), user_agent: DEFAULT_USER_AGENT.into(), devtools_chan: dc, - filemanager: FileManager::new(), + filemanager: FileManager::new(sender), cancellation_listener: Arc::new(Mutex::new(CancellationListener::new(None))), } } @@ -76,7 +106,7 @@ impl FetchTaskTarget for FetchResponseCollector { } fn fetch(request: &mut Request, dc: Option>) -> Response { - fetch_with_context(request, &new_fetch_context(dc)) + fetch_with_context(request, &new_fetch_context(dc, None)) } fn fetch_with_context(request: &mut Request, context: &FetchContext) -> Response { @@ -96,7 +126,7 @@ fn fetch_with_cors_cache(request: &mut Request, cache: &mut CorsCache) -> Respon sender: sender, }; - methods::fetch_with_cors_cache(request, cache, &mut target, &new_fetch_context(None)); + methods::fetch_with_cors_cache(request, cache, &mut target, &new_fetch_context(None, None)); receiver.recv().unwrap() } diff --git a/components/net/tests/resource_thread.rs b/components/net/tests/resource_thread.rs index adf1e305698..575b67dc7de 100644 --- a/components/net/tests/resource_thread.rs +++ b/components/net/tests/resource_thread.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use create_embedder_proxy; use ipc_channel::ipc; use net::resource_thread::new_core_resource_thread; use net::test::parse_hostsfile; @@ -20,7 +21,7 @@ fn test_exit() { let (mtx, _mrx) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap(); let (resource_thread, _private_resource_thread) = new_core_resource_thread( - "".into(), None, ProfilerChan(tx), MemProfilerChan(mtx), None); + "".into(), None, ProfilerChan(tx), MemProfilerChan(mtx), create_embedder_proxy(), None); resource_thread.send(CoreResourceMsg::Exit(sender)).unwrap(); receiver.recv().unwrap(); } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index e0e8f41b1b3..bc06dc84ca7 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -462,6 +462,7 @@ fn create_constellation(user_agent: Cow<'static, str>, devtools_chan.clone(), time_profiler_chan.clone(), mem_profiler_chan.clone(), + embedder_proxy.clone(), config_dir); let font_cache_thread = FontCacheThread::new(public_resource_threads.sender(), webrender_api_sender.create_api()); diff --git a/ports/servo/browser.rs b/ports/servo/browser.rs index 429b59b189b..6d83c59153d 100644 --- a/ports/servo/browser.rs +++ b/ports/servo/browser.rs @@ -10,14 +10,16 @@ use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent}; use servo::ipc_channel::ipc::IpcSender; use servo::msg::constellation_msg::{Key, TopLevelBrowsingContextId as BrowserId}; use servo::msg::constellation_msg::{KeyModifiers, KeyState, TraversalDirection}; +use servo::net_traits::filemanager_thread::FilterPattern; use servo::net_traits::pub_domains::is_reg_domain; use servo::script_traits::TouchEventType; +use servo::servo_config::opts; use servo::servo_config::prefs::PREFS; use servo::servo_url::ServoUrl; use servo::webrender_api::ScrollLocation; use std::mem; use std::rc::Rc; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] use std::thread; use tinyfiledialogs; @@ -294,6 +296,12 @@ impl Browser { }, EmbedderMsg::GetSelectedBluetoothDevice(devices, sender) => { platform_get_selected_devices(devices, sender); + }, + EmbedderMsg::SelectFiles(patterns, multiple_files, sender) => { + if opts::get().headless { + let _ = sender.send(None); + } + platform_get_selected_files(patterns, multiple_files, sender); } EmbedderMsg::ShowIME(_browser_id, _kind) => { debug!("ShowIME received"); @@ -340,6 +348,40 @@ fn platform_get_selected_devices(devices: Vec, sender: IpcSender, + multiple_files: bool, + sender: IpcSender>>) { + let picker_name = if multiple_files { "Pick files" } else { "Pick a file" }; + + thread::Builder::new().name(picker_name.to_owned()).spawn(move || { + let mut filter = vec![]; + for p in patterns { + let s = "*.".to_string() + &p.0; + filter.push(s) + } + + let filter_ref = &(filter.iter().map(|s| s.as_str()).collect::>()[..]); + let filter_opt = if filter.len() > 0 { Some((filter_ref, "")) } else { None }; + + if multiple_files { + let files = tinyfiledialogs::open_file_dialog_multi(picker_name, "", filter_opt); + let _ = sender.send(files); + } else { + let file = tinyfiledialogs::open_file_dialog(picker_name, "", filter_opt); + let _ = sender.send(file.map(|x| vec![x])); + } + }).expect("Thread spawning failed"); +} + +#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))] +fn platform_get_selected_files(_patterns: Vec, + _multiple_files: bool, + sender: IpcSender>>) { + warn!("File picker not implemented"); + let _ = sender.send(None); +} + fn sanitize_url(request: &str) -> Option { let request = request.trim(); ServoUrl::parse(&request).ok()