diff --git a/README.md b/README.md index de6524223aa..934236c17f7 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ On Debian-based Linuxes: sudo apt-get install curl freeglut3-dev \ libfreetype6-dev libgl1-mesa-dri libglib2.0-dev xorg-dev \ msttcorefonts gperf g++ cmake python-virtualenv \ - libssl-dev libbz2-dev libosmesa6-dev + libssl-dev libbz2-dev libosmesa6-dev libxmu6 libxmu-dev ``` On Fedora: @@ -37,7 +37,7 @@ On Fedora: sudo yum install curl freeglut-devel libtool gcc-c++ libXi-devel \ freetype-devel mesa-libGL-devel glib2-devel libX11-devel libXrandr-devel gperf \ fontconfig-devel cabextract ttmkfdir python python-virtualenv expat-devel \ - rpm-build openssl-devel cmake bzip2-devel libXcursor-devel + rpm-build openssl-devel cmake bzip2-devel libXcursor-devel libXmu-devel pushd /tmp wget http://corefonts.sourceforge.net/msttcorefonts-2.5-1.spec rpmbuild -bb msttcorefonts-2.5-1.spec @@ -48,7 +48,7 @@ popd On Arch Linux: ``` sh -sudo pacman -S --needed base-devel git python2 python2-virtualenv mesa ttf-font cmake bzip2 +sudo pacman -S --needed base-devel git python2 python2-virtualenv mesa ttf-font cmake bzip2 libxmu ``` Cross-compilation for Android: diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index d9fc51499fb..37035dec23f 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -58,6 +58,9 @@ git = "https://github.com/servo/rust-core-text" [dependencies.gleam] git = "https://github.com/servo/gleam" +[dependencies.clipboard] +git = "https://github.com/aweinstock314/rust-x11-clipboard" + [dependencies] url = "0.2.16" time = "0.1.17" diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 0520fa4859f..5867a871096 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -38,6 +38,7 @@ use util::cursor::Cursor; use util::geometry::PagePx; use util::opts; use util::task::spawn_named; +use clipboard::ClipboardContext; /// Maintains the pipelines and navigation context and grants permission to composite. pub struct Constellation { @@ -102,6 +103,9 @@ pub struct Constellation { phantom: PhantomData<(LTF, STF)>, pub window_size: WindowSizeData, + + /// Means of accessing the clipboard + clipboard_ctx: ClipboardContext, } /// Stores the navigation context for a single frame in the frame tree. @@ -212,6 +216,7 @@ impl Constellation { device_pixel_ratio: ScaleFactor::new(1.0), }, phantom: PhantomData, + clipboard_ctx: ClipboardContext::new().unwrap(), }; constellation.run(); }); @@ -395,6 +400,16 @@ impl Constellation { debug!("constellation got focus message"); self.handle_focus_msg(pipeline_id); } + ConstellationMsg::GetClipboardContents(sender) => { + let result = match self.clipboard_ctx.get_contents() { + Ok(s) => s, + Err(e) => { + debug!("Error getting clipboard contents ({}), defaulting to empty string", e); + "".to_string() + }, + }; + sender.send(result).unwrap(); + } } true } diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index e98f03f4a73..f3f54cad388 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -28,6 +28,7 @@ extern crate net_traits; extern crate util; extern crate gleam; extern crate webdriver_server; +extern crate clipboard; extern crate libc; extern crate time; diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 0f911a7e523..6029541336f 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -229,6 +229,8 @@ pub enum Msg { GetRootPipeline(Sender>), /// Notifies the constellation that this frame has received focus. FocusMsg(PipelineId), + /// Requests that the constellation retrieve the current contents of the clipboard + GetClipboardContents(Sender), } // https://developer.mozilla.org/en-US/docs/Web/API/Using_the_Browser_API#Events diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 8d492ab8c65..1f8abb17992 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -29,6 +29,7 @@ use dom::htmlformelement::{SubmittedFrom, ResetFrom}; use dom::node::{DisabledStateHelpers, Node, NodeHelpers, NodeDamage, NodeTypeId}; use dom::node::{document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use textinput::TextInput; use textinput::KeyReaction::{TriggerDefaultAction, DispatchInput, Nothing}; use textinput::Lines::Single; @@ -109,6 +110,7 @@ static DEFAULT_INPUT_SIZE: u32 = 20; impl HTMLInputElement { fn new_inherited(localName: DOMString, prefix: Option, document: JSRef) -> HTMLInputElement { + let chan = document.window().root().r().constellation_chan(); HTMLInputElement { htmlelement: HTMLElement::new_inherited(HTMLElementTypeId::HTMLInputElement, localName, prefix, document), input_type: Cell::new(InputType::InputText), @@ -118,7 +120,7 @@ impl HTMLInputElement { checked_changed: Cell::new(false), value_changed: Cell::new(false), size: Cell::new(DEFAULT_INPUT_SIZE), - textinput: DOMRefCell::new(TextInput::new(Single, "".to_owned())), + textinput: DOMRefCell::new(TextInput::new(Single, "".to_owned(), Some(chan))), activation_state: DOMRefCell::new(InputActivationState::new()) } } diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index 41a1da8620e..738c906e91b 100644 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -27,6 +27,7 @@ use dom::node::{DisabledStateHelpers, Node, NodeHelpers, NodeDamage, NodeTypeId} use dom::node::{document_from_node, window_from_node}; use textinput::{TextInput, Lines, KeyReaction}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use script_task::{ScriptMsg, Runnable}; use util::str::DOMString; @@ -90,9 +91,10 @@ static DEFAULT_ROWS: u32 = 2; impl HTMLTextAreaElement { fn new_inherited(localName: DOMString, prefix: Option, document: JSRef) -> HTMLTextAreaElement { + let chan = document.window().root().r().constellation_chan(); HTMLTextAreaElement { htmlelement: HTMLElement::new_inherited(HTMLElementTypeId::HTMLTextAreaElement, localName, prefix, document), - textinput: DOMRefCell::new(TextInput::new(Lines::Multiple, "".to_owned())), + textinput: DOMRefCell::new(TextInput::new(Lines::Multiple, "".to_owned(), Some(chan))), cols: Cell::new(DEFAULT_COLS), rows: Cell::new(DEFAULT_ROWS), value_changed: Cell::new(false), diff --git a/components/script/textinput.rs b/components/script/textinput.rs index c26549bff73..33c8ce90665 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -6,6 +6,8 @@ use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods; use dom::bindings::js::JSRef; +use msg::constellation_msg::ConstellationChan; +use msg::constellation_msg::Msg as ConstellationMsg; use dom::keyboardevent::KeyboardEvent; use util::str::DOMString; @@ -13,6 +15,7 @@ use std::borrow::ToOwned; use std::cmp::{min, max}; use std::default::Default; use std::num::SignedInt; +use std::sync::mpsc::channel; #[derive(Copy, PartialEq)] pub enum Selection { @@ -40,6 +43,7 @@ pub struct TextInput { selection_begin: Option, /// Is this a multiline input? multiline: bool, + constellation_channel: Option } /// Resulting action to be taken by the owner of a text input that is handling an event. @@ -87,12 +91,13 @@ fn is_control_key(event: JSRef) -> bool { impl TextInput { /// Instantiate a new text input control - pub fn new(lines: Lines, initial: DOMString) -> TextInput { + pub fn new(lines: Lines, initial: DOMString, cc: Option) -> TextInput { let mut i = TextInput { lines: vec!(), edit_point: Default::default(), selection_begin: None, multiline: lines == Lines::Multiple, + constellation_channel: cc, }; i.set_content(initial); i @@ -118,6 +123,15 @@ impl TextInput { self.replace_selection(ch.to_string()); } + /// Insert a string at the current editing point + fn insert_string(&mut self, s: &str) { + // it looks like this could be made performant by avoiding some redundant + // selection-related checks, but use the simple implementation for now + for ch in s.chars() { + self.insert_char(ch); + } + } + pub fn get_sorted_selection(&self) -> (TextPoint, TextPoint) { let begin = self.selection_begin.unwrap(); let end = self.edit_point; @@ -282,10 +296,22 @@ impl TextInput { self.select_all(); KeyReaction::Nothing }, + "v" if is_control_key(event) => { + let (tx, rx) = channel(); + let mut contents = None; + if let Some(ref cc) = self.constellation_channel { + cc.0.send(ConstellationMsg::GetClipboardContents(tx)).unwrap(); + contents = Some(rx.recv().unwrap()); + } + if let Some(contents) = contents { + self.insert_string(contents.as_slice()); + } + KeyReaction::DispatchInput + }, // printable characters have single-character key values c if c.len() == 1 => { self.insert_char(c.char_at(0)); - return KeyReaction::DispatchInput; + KeyReaction::DispatchInput } "Space" => { self.insert_char(' '); diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index a984dcf45a4..46cf8773747 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -79,6 +79,15 @@ dependencies = [ "gleam 0.0.1 (git+https://github.com/servo/gleam)", ] +[[package]] +name = "clipboard" +version = "0.0.1" +source = "git+https://github.com/aweinstock314/rust-x11-clipboard#eae9596e7e407c8b6037b934c1a8e42a309423fd" +dependencies = [ + "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "xlib 0.1.0 (git+https://github.com/servo/rust-xlib)", +] + [[package]] name = "clock_ticks" version = "0.0.4" @@ -98,6 +107,7 @@ name = "compositing" version = "0.0.1" dependencies = [ "azure 0.1.0 (git+https://github.com/servo/rust-azure)", + "clipboard 0.0.1 (git+https://github.com/aweinstock314/rust-x11-clipboard)", "core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)", "core_text 0.1.0 (git+https://github.com/servo/rust-core-text)", "devtools_traits 0.0.1", @@ -1077,7 +1087,7 @@ dependencies = [ [[package]] name = "xlib" version = "0.1.0" -source = "git+https://github.com/servo/rust-xlib#715e6f9bb3dfcd925996caedeb77aefb31c2bd65" +source = "git+https://github.com/servo/rust-xlib#1a0f3d48fbebf96e2d1bf83ac71309b27f49e0c7" dependencies = [ "bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 2e6a5fbe6ec..292f9c0e2af 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -77,6 +77,15 @@ dependencies = [ "gleam 0.0.1 (git+https://github.com/servo/gleam)", ] +[[package]] +name = "clipboard" +version = "0.0.1" +source = "git+https://github.com/aweinstock314/rust-x11-clipboard#eae9596e7e407c8b6037b934c1a8e42a309423fd" +dependencies = [ + "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "xlib 0.1.0 (git+https://github.com/servo/rust-xlib)", +] + [[package]] name = "clock_ticks" version = "0.0.4" @@ -96,6 +105,7 @@ name = "compositing" version = "0.0.1" dependencies = [ "azure 0.1.0 (git+https://github.com/servo/rust-azure)", + "clipboard 0.0.1 (git+https://github.com/aweinstock314/rust-x11-clipboard)", "core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)", "core_text 0.1.0 (git+https://github.com/servo/rust-core-text)", "devtools_traits 0.0.1", @@ -1049,7 +1059,7 @@ dependencies = [ [[package]] name = "xlib" version = "0.1.0" -source = "git+https://github.com/servo/rust-xlib#715e6f9bb3dfcd925996caedeb77aefb31c2bd65" +source = "git+https://github.com/servo/rust-xlib#1a0f3d48fbebf96e2d1bf83ac71309b27f49e0c7" dependencies = [ "bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index 6d3715bbd7e..0f29369ed46 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -71,6 +71,15 @@ dependencies = [ "gleam 0.0.1 (git+https://github.com/servo/gleam)", ] +[[package]] +name = "clipboard" +version = "0.0.1" +source = "git+https://github.com/aweinstock314/rust-x11-clipboard#eae9596e7e407c8b6037b934c1a8e42a309423fd" +dependencies = [ + "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "xlib 0.1.0 (git+https://github.com/servo/rust-xlib)", +] + [[package]] name = "clock_ticks" version = "0.0.4" @@ -91,6 +100,7 @@ name = "compositing" version = "0.0.1" dependencies = [ "azure 0.1.0 (git+https://github.com/servo/rust-azure)", + "clipboard 0.0.1 (git+https://github.com/aweinstock314/rust-x11-clipboard)", "core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)", "core_text 0.1.0 (git+https://github.com/servo/rust-core-text)", "devtools_traits 0.0.1", @@ -1035,7 +1045,7 @@ dependencies = [ [[package]] name = "xlib" version = "0.1.0" -source = "git+https://github.com/servo/rust-xlib#715e6f9bb3dfcd925996caedeb77aefb31c2bd65" +source = "git+https://github.com/servo/rust-xlib#1a0f3d48fbebf96e2d1bf83ac71309b27f49e0c7" dependencies = [ "bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/tests/unit/script/textinput.rs b/tests/unit/script/textinput.rs index 322ee63c1c5..0d7326685db 100644 --- a/tests/unit/script/textinput.rs +++ b/tests/unit/script/textinput.rs @@ -12,7 +12,7 @@ use std::borrow::ToOwned; #[test] fn test_textinput_delete_char() { - let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned()); + let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned(), None); textinput.adjust_horizontal(2, Selection::NotSelected); textinput.delete_char(DeleteDir::Backward); assert_eq!(textinput.get_content(), "acdefg"); @@ -27,7 +27,7 @@ fn test_textinput_delete_char() { #[test] fn test_textinput_insert_char() { - let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned()); + let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned(), None); textinput.adjust_horizontal(2, Selection::NotSelected); textinput.insert_char('a'); assert_eq!(textinput.get_content(), "abacdefg"); @@ -39,7 +39,7 @@ fn test_textinput_insert_char() { #[test] fn test_textinput_get_sorted_selection() { - let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned()); + let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned(), None); textinput.adjust_horizontal(2, Selection::NotSelected); textinput.adjust_horizontal(2, Selection::Selected); let (begin, end) = textinput.get_sorted_selection(); @@ -56,7 +56,7 @@ fn test_textinput_get_sorted_selection() { #[test] fn test_textinput_replace_selection() { - let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned()); + let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned(), None); textinput.adjust_horizontal(2, Selection::NotSelected); textinput.adjust_horizontal(2, Selection::Selected); @@ -66,7 +66,7 @@ fn test_textinput_replace_selection() { #[test] fn test_textinput_current_line_length() { - let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned()); + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned(), None); assert_eq!(textinput.current_line_length(), 3); textinput.adjust_vertical(1, Selection::NotSelected); @@ -78,7 +78,7 @@ fn test_textinput_current_line_length() { #[test] fn test_textinput_adjust_vertical() { - let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned()); + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned(), None); textinput.adjust_horizontal(3, Selection::NotSelected); textinput.adjust_vertical(1, Selection::NotSelected); assert_eq!(textinput.edit_point.line, 1); @@ -95,7 +95,7 @@ fn test_textinput_adjust_vertical() { #[test] fn test_textinput_adjust_horizontal() { - let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned()); + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned(), None); textinput.adjust_horizontal(4, Selection::NotSelected); assert_eq!(textinput.edit_point.line, 1); assert_eq!(textinput.edit_point.index, 0); @@ -115,12 +115,12 @@ fn test_textinput_adjust_horizontal() { #[test] fn test_textinput_handle_return() { - let mut single_line_textinput = TextInput::new(Lines::Single, "abcdef".to_owned()); + let mut single_line_textinput = TextInput::new(Lines::Single, "abcdef".to_owned(), None); single_line_textinput.adjust_horizontal(3, Selection::NotSelected); single_line_textinput.handle_return(); assert_eq!(single_line_textinput.get_content(), "abcdef"); - let mut multi_line_textinput = TextInput::new(Lines::Multiple, "abcdef".to_owned()); + let mut multi_line_textinput = TextInput::new(Lines::Multiple, "abcdef".to_owned(), None); multi_line_textinput.adjust_horizontal(3, Selection::NotSelected); multi_line_textinput.handle_return(); assert_eq!(multi_line_textinput.get_content(), "abc\ndef"); @@ -128,7 +128,7 @@ fn test_textinput_handle_return() { #[test] fn test_textinput_select_all() { - let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned()); + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned(), None); assert_eq!(textinput.edit_point.line, 0); assert_eq!(textinput.edit_point.index, 0); @@ -139,16 +139,16 @@ fn test_textinput_select_all() { #[test] fn test_textinput_get_content() { - let single_line_textinput = TextInput::new(Lines::Single, "abcdefg".to_owned()); + let single_line_textinput = TextInput::new(Lines::Single, "abcdefg".to_owned(), None); assert_eq!(single_line_textinput.get_content(), "abcdefg"); - let multi_line_textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned()); + let multi_line_textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned(), None); assert_eq!(multi_line_textinput.get_content(), "abc\nde\nf"); } #[test] fn test_textinput_set_content() { - let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned()); + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned(), None); assert_eq!(textinput.get_content(), "abc\nde\nf"); textinput.set_content("abc\nf".to_owned());