mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Auto merge of #20480 - kwonoj:refactor-file-manager, r=paulrouget
refactor(net): removes direct ui invocation from filemgr thread <!-- Please describe your changes on the following line: --> - relates to https://github.com/servo/servo/issues/20428. This PR tries to refactor `net::filemanager_thread` implementation, removes direct ui-related code invocation but ask constellation. I believe overall organization might need to be refactored still as I took my own liberty to wire up dots and dots between components, which could be non-recommended practices. Probably point of review / need to be updated are 1. Communication between components Currently it's wired as like below: ``` +----------------+ +---------------+ +---------+ | | constellationMsg | | embedderMsg | | | filemgr_thread +------------------->+ constellation +------------->+ browser | | | | | | | +-----+----------+ +---------------+ +----+----+ ^ | +-------------------------------------------------------------------+ filelist: Vec(String) ``` - is this feasible approach? - does organization of message / fn (where to put consteallation / embedder msg & fns) are legit? 2. Removal of `filemanger_thread::UIProvider` - As filemanager_thread no longer need to aware actual ui, this PR removes `UIProvider` completely and let invoke tinyfiledialog directly in message listener. Maybe UIProvider itself still being needed? 3. Overall fn organization - To reduce duplicated code it takes single msg with boolean flag to distinguish selecting multiple files, may feasible / or better to create explicit paths between two. 4. Invoking tfd in a separate thread - This was mainly to align behavior to previous implentation, where tfd was invoked inside of filemanager_thread so does not block main. It may possibly just let block instead. and of course, a lot of other codes need to follow better practices. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #20428 (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ - Manually verified file picker `<input type=files>` can pick up files. <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20480) <!-- Reviewable:end -->
This commit is contained in:
commit
bf667677f7
13 changed files with 160 additions and 147 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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<String>),
|
||||
/// Open dialog to select bluetooth device.
|
||||
GetSelectedBluetoothDevice(Vec<String>, IpcSender<Option<String>>),
|
||||
/// Open file dialog to select files. Set boolean flag to true allows to select multiple files.
|
||||
SelectFiles(Vec<FilterPattern>, bool, IpcSender<Option<Vec<String>>>),
|
||||
/// 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"),
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<FilterPattern>) -> Option<String>;
|
||||
|
||||
fn open_file_dialog_multi(&self, path: &str, patterns: Vec<FilterPattern>) -> Option<Vec<String>>;
|
||||
}
|
||||
|
||||
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<FilterPattern>) -> Option<String> {
|
||||
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::<Vec<&str>>()[..]);
|
||||
|
||||
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<FilterPattern>) -> Option<Vec<String>> {
|
||||
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::<Vec<&str>>()[..]);
|
||||
|
||||
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<FilterPattern>) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
|
||||
fn open_file_dialog_multi(&self, _path: &str, _patterns: Vec<FilterPattern>) -> Option<Vec<String>> {
|
||||
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<FileManagerStore>,
|
||||
}
|
||||
|
||||
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<UI>(&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<UI>(&self,
|
||||
patterns: Vec<FilterPattern>,
|
||||
sender: IpcSender<FileManagerResult<SelectedFile>>,
|
||||
origin: FileOrigin,
|
||||
opt_test_path: Option<String>,
|
||||
ui: &UI)
|
||||
where UI: UIProvider,
|
||||
{
|
||||
fn query_files_from_embedder(&self,
|
||||
patterns: Vec<FilterPattern>,
|
||||
multiple_files: bool,
|
||||
embedder_proxy: EmbedderProxy) -> Option<Vec<String>> {
|
||||
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<FilterPattern>,
|
||||
sender: IpcSender<FileManagerResult<SelectedFile>>,
|
||||
origin: FileOrigin,
|
||||
opt_test_path: Option<String>,
|
||||
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<UI>(&self,
|
||||
patterns: Vec<FilterPattern>,
|
||||
sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>,
|
||||
origin: FileOrigin,
|
||||
opt_test_paths: Option<Vec<String>>,
|
||||
ui: &UI)
|
||||
where UI: UIProvider,
|
||||
{
|
||||
fn select_files(&self,
|
||||
patterns: Vec<FilterPattern>,
|
||||
sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>,
|
||||
origin: FileOrigin,
|
||||
opt_test_paths: Option<Vec<String>>,
|
||||
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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Sender<DevtoolsControlMsg>>,
|
||||
time_profiler_chan: ProfilerChan,
|
||||
mem_profiler_chan: MemProfilerChan,
|
||||
embedder_proxy: EmbedderProxy,
|
||||
config_dir: Option<PathBuf>)
|
||||
-> (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<StorageThreadMsg> = 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<Sender<DevtoolsControlMsg>>,
|
||||
time_profiler_chan: ProfilerChan,
|
||||
mem_profiler_chan: MemProfilerChan,
|
||||
embedder_proxy: EmbedderProxy,
|
||||
config_dir: Option<PathBuf>)
|
||||
-> (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<Sender<DevtoolsControlMsg>>,
|
||||
_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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))),
|
||||
};
|
||||
|
||||
|
|
|
@ -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<FilterPattern>) -> Option<String> {
|
||||
Some("tests/test.jpeg".to_string())
|
||||
}
|
||||
|
||||
fn open_file_dialog_multi(&self, _path: &str, _patterns: Vec<FilterPattern>) -> Option<Vec<String>> {
|
||||
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");
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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<Response>,
|
||||
}
|
||||
|
||||
fn new_fetch_context(dc: Option<Sender<DevtoolsControlMsg>>) -> 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<EventLoopWaker + Send> {
|
||||
Box::new(DummyEventLoopWaker { })
|
||||
}
|
||||
}
|
||||
|
||||
Box::new(DummyEventLoopWaker::new())
|
||||
};
|
||||
|
||||
EmbedderProxy {
|
||||
sender: sender,
|
||||
event_loop_waker: event_loop_waker()
|
||||
}
|
||||
}
|
||||
|
||||
fn new_fetch_context(dc: Option<Sender<DevtoolsControlMsg>>, fc: Option<EmbedderProxy>) -> 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<Sender<DevtoolsControlMsg>>) -> 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()
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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<String>, sender: IpcSender<Option<
|
|||
let _ = sender.send(None);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
fn platform_get_selected_files(patterns: Vec<FilterPattern>,
|
||||
multiple_files: bool,
|
||||
sender: IpcSender<Option<Vec<String>>>) {
|
||||
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::<Vec<&str>>()[..]);
|
||||
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<FilterPattern>,
|
||||
_multiple_files: bool,
|
||||
sender: IpcSender<Option<Vec<String>>>) {
|
||||
warn!("File picker not implemented");
|
||||
let _ = sender.send(None);
|
||||
}
|
||||
|
||||
fn sanitize_url(request: &str) -> Option<ServoUrl> {
|
||||
let request = request.trim();
|
||||
ServoUrl::parse(&request).ok()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue