diff options
Diffstat (limited to 'components')
32 files changed, 603 insertions, 879 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 52c481f504e..7a3a09201c6 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1738,51 +1738,89 @@ impl BlockFlowDisplayListBuilding for BlockFlow { } pub trait InlineFlowDisplayListBuilding { + fn build_display_list_for_inline_fragment_at_index(&mut self, + index: usize, + display_list: &mut DisplayList, + layout_context: &LayoutContext); fn build_display_list_for_inline(&mut self, layout_context: &LayoutContext); } impl InlineFlowDisplayListBuilding for InlineFlow { + fn build_display_list_for_inline_fragment_at_index(&mut self, + index: usize, + display_list: &mut DisplayList, + layout_context: &LayoutContext) { + let fragment = self.fragments.fragments.get_mut(index).unwrap(); + fragment.build_display_list(display_list, + layout_context, + &self.base.stacking_relative_position, + &self.base + .early_absolute_position_info + .relative_containing_block_size, + self.base + .early_absolute_position_info + .relative_containing_block_mode, + BorderPaintingMode::Separate, + BackgroundAndBorderLevel::Content, + &self.base.clip, + &self.base.stacking_relative_position_of_display_port); + + match fragment.specific { + SpecificFragmentInfo::InlineBlock(ref mut block_flow) => { + let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); + display_list.append_from( + &mut flow::mut_base(block_flow).display_list_building_result) + } + SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut block_flow) => { + let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); + display_list.append_from( + &mut flow::mut_base(block_flow).display_list_building_result) + } + SpecificFragmentInfo::InlineAbsolute(ref mut block_flow) => { + let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); + display_list.append_from( + &mut flow::mut_base(block_flow).display_list_building_result) + } + _ => {} + } + } + fn build_display_list_for_inline(&mut self, layout_context: &LayoutContext) { // TODO(#228): Once we form lines and have their cached bounds, we can be smarter and // not recurse on a line if nothing in it can intersect the dirty region. debug!("Flow: building display list for {} inline fragments", self.fragments.len()); let mut display_list = box DisplayList::new(); - let mut has_stacking_context = false; - for fragment in &mut self.fragments.fragments { - fragment.build_display_list(&mut *display_list, - layout_context, - &self.base.stacking_relative_position, - &self.base - .early_absolute_position_info - .relative_containing_block_size, - self.base - .early_absolute_position_info - .relative_containing_block_mode, - BorderPaintingMode::Separate, - BackgroundAndBorderLevel::Content, - &self.base.clip, - &self.base.stacking_relative_position_of_display_port); - - has_stacking_context = fragment.establishes_stacking_context(); - - match fragment.specific { - SpecificFragmentInfo::InlineBlock(ref mut block_flow) => { - let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); - display_list.append_from( - &mut flow::mut_base(block_flow).display_list_building_result) - } - SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut block_flow) => { - let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); - display_list.append_from( - &mut flow::mut_base(block_flow).display_list_building_result) - } - SpecificFragmentInfo::InlineAbsolute(ref mut block_flow) => { - let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); - display_list.append_from( - &mut flow::mut_base(block_flow).display_list_building_result) + + // We iterate using an index here, because we want to avoid doing a doing + // a double-borrow of self (one mutable for the method call and one immutable + // for the self.fragments.fragment iterator itself). + for index in (0..self.fragments.fragments.len()) { + let establishes_stacking_context = { + let fragment = self.fragments.fragments.get(index).unwrap(); + match fragment.specific { + SpecificFragmentInfo::InlineBlock(_) | + SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => false, + _ => fragment.establishes_stacking_context(), } - _ => {} + }; + + if establishes_stacking_context { + let mut fragment_display_list = box DisplayList::new(); + self.build_display_list_for_inline_fragment_at_index(index, + &mut *fragment_display_list, + layout_context); + let fragment = self.fragments.fragments.get(index).unwrap(); + display_list.positioned_content.push_back(DisplayItem::StackingContextClass( + fragment.create_stacking_context( + &self.base, + fragment_display_list, + ScrollPolicy::Scrollable, + StackingContextCreationMode::Normal))); + } else { + self.build_display_list_for_inline_fragment_at_index(index, + &mut *display_list, + layout_context); } } @@ -1791,30 +1829,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow { self.fragments.fragments[0].node); } - // FIXME(Savago): fix Fragment::establishes_stacking_context() for absolute positioned item - // and remove the check for filter presence. Further details on #5812. - // - // FIXME(#7424, pcwalton): This is terribly bogus! What is even going on here? - if has_stacking_context { - match self.fragments.fragments[0].specific { - SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) => {} - _ => { - has_stacking_context = - !self.fragments.fragments[0].style().get_effects().filter.is_empty() - } - } - } - - self.base.display_list_building_result = if has_stacking_context { - Some(DisplayList::new_with_stacking_context( - self.fragments.fragments[0].create_stacking_context( - &self.base, - display_list, - ScrollPolicy::Scrollable, - StackingContextCreationMode::Normal))) - } else { - Some(display_list) - }; + self.base.display_list_building_result = Some(display_list); if opts::get().validate_display_list_geometry { self.base.validate_display_list_geometry(); diff --git a/components/net/about_loader.rs b/components/net/about_loader.rs index 04878fed70b..6a03aeb041d 100644 --- a/components/net/about_loader.rs +++ b/components/net/about_loader.rs @@ -9,13 +9,16 @@ use hyper::mime::{Mime, SubLevel, TopLevel}; use mime_classifier::MIMEClassifier; use net_traits::ProgressMsg::Done; use net_traits::{LoadConsumer, LoadData, Metadata}; -use resource_task::{send_error, start_sending_sniffed_opt}; +use resource_task::{CancellationListener, send_error, start_sending_sniffed_opt}; use std::fs::PathExt; use std::sync::Arc; use url::Url; use util::resource_files::resources_dir_path; -pub fn factory(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEClassifier>) { +pub fn factory(mut load_data: LoadData, + start_chan: LoadConsumer, + classifier: Arc<MIMEClassifier>, + cancel_listener: CancellationListener) { match load_data.url.non_relative_scheme_data().unwrap() { "blank" => { let metadata = Metadata { @@ -42,5 +45,5 @@ pub fn factory(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Ar return } }; - file_loader::factory(load_data, start_chan, classifier) + file_loader::factory(load_data, start_chan, classifier, cancel_listener) } diff --git a/components/net/data_loader.rs b/components/net/data_loader.rs index f2f72bac88e..b7fb25c124d 100644 --- a/components/net/data_loader.rs +++ b/components/net/data_loader.rs @@ -6,21 +6,27 @@ use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; use mime_classifier::MIMEClassifier; use net_traits::ProgressMsg::{Done, Payload}; use net_traits::{LoadConsumer, LoadData, Metadata}; -use resource_task::{send_error, start_sending_sniffed_opt}; +use resource_task::{CancellationListener, send_error, start_sending_sniffed_opt}; use rustc_serialize::base64::FromBase64; use std::sync::Arc; use url::SchemeData; use url::percent_encoding::percent_decode; -pub fn factory(load_data: LoadData, senders: LoadConsumer, classifier: Arc<MIMEClassifier>) { +pub fn factory(load_data: LoadData, + senders: LoadConsumer, + classifier: Arc<MIMEClassifier>, + cancel_listener: CancellationListener) { // NB: we don't spawn a new task. // Hypothesis: data URLs are too small for parallel base64 etc. to be worth it. // Should be tested at some point. // Left in separate function to allow easy moving to a task, if desired. - load(load_data, senders, classifier) + load(load_data, senders, classifier, cancel_listener) } -pub fn load(load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEClassifier>) { +pub fn load(load_data: LoadData, + start_chan: LoadConsumer, + classifier: Arc<MIMEClassifier>, + cancel_listener: CancellationListener) { let url = load_data.url; assert!(&*url.scheme == "data"); @@ -63,8 +69,11 @@ pub fn load(load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEC vec!((Attr::Charset, Value::Ext("US-ASCII".to_owned()))))); } - let bytes = percent_decode(parts[1].as_bytes()); + if cancel_listener.is_cancelled() { + return; + } + let bytes = percent_decode(parts[1].as_bytes()); let bytes = if is_base64 { // FIXME(#2909): It’s unclear what to do with non-alphabet characters, // but Acid 3 apparently depends on spaces being ignored. diff --git a/components/net/file_loader.rs b/components/net/file_loader.rs index c41f382f949..a4348049d78 100644 --- a/components/net/file_loader.rs +++ b/components/net/file_loader.rs @@ -5,7 +5,8 @@ use mime_classifier::MIMEClassifier; use net_traits::ProgressMsg::{Done, Payload}; use net_traits::{LoadConsumer, LoadData, Metadata}; -use resource_task::{ProgressSender, send_error, start_sending_sniffed, start_sending_sniffed_opt}; +use resource_task::{CancellationListener, ProgressSender}; +use resource_task::{send_error, start_sending_sniffed, start_sending_sniffed_opt}; use std::borrow::ToOwned; use std::error::Error; use std::fs::File; @@ -21,6 +22,11 @@ enum ReadStatus { EOF, } +enum LoadResult { + Cancelled, + Finished, +} + fn read_block(reader: &mut File) -> Result<ReadStatus, String> { let mut buf = vec![0; READ_SIZE]; match reader.read(&mut buf) { @@ -33,17 +39,24 @@ fn read_block(reader: &mut File) -> Result<ReadStatus, String> { } } -fn read_all(reader: &mut File, progress_chan: &ProgressSender) - -> Result<(), String> { +fn read_all(reader: &mut File, progress_chan: &ProgressSender, cancel_listener: &CancellationListener) + -> Result<LoadResult, String> { loop { + if cancel_listener.is_cancelled() { + return Ok(LoadResult::Cancelled); + } + match try!(read_block(reader)) { ReadStatus::Partial(buf) => progress_chan.send(Payload(buf)).unwrap(), - ReadStatus::EOF => return Ok(()), + ReadStatus::EOF => return Ok(LoadResult::Finished), } } } -pub fn factory(load_data: LoadData, senders: LoadConsumer, classifier: Arc<MIMEClassifier>) { +pub fn factory(load_data: LoadData, + senders: LoadConsumer, + classifier: Arc<MIMEClassifier>, + cancel_listener: CancellationListener) { let url = load_data.url; assert!(&*url.scheme == "file"); spawn_named("file_loader".to_owned(), move || { @@ -52,14 +65,22 @@ pub fn factory(load_data: LoadData, senders: LoadConsumer, classifier: Arc<MIMEC Ok(file_path) => { match File::open(&file_path) { Ok(ref mut reader) => { + if cancel_listener.is_cancelled() { + return; + } match read_block(reader) { Ok(ReadStatus::Partial(buf)) => { let metadata = Metadata::default(url); let progress_chan = start_sending_sniffed(senders, metadata, classifier, &buf); progress_chan.send(Payload(buf)).unwrap(); - let res = read_all(reader, &progress_chan); - let _ = progress_chan.send(Done(res)); + let read_result = read_all(reader, &progress_chan, &cancel_listener); + if let Ok(load_result) = read_result { + match load_result { + LoadResult::Cancelled => return, + LoadResult::Finished => progress_chan.send(Done(Ok(()))).unwrap(), + } + } } Ok(ReadStatus::EOF) => { let metadata = Metadata::default(url); diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index 15ed5f7fdf4..ef854a504dd 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -28,7 +28,7 @@ use net_traits::ProgressMsg::{Done, Payload}; use net_traits::hosts::replace_hosts; use net_traits::{CookieSource, IncludeSubdomains, LoadConsumer, LoadData, Metadata}; use openssl::ssl::{SSL_VERIFY_PEER, SslContext, SslMethod}; -use resource_task::{send_error, start_sending_sniffed_opt}; +use resource_task::{CancellationListener, send_error, start_sending_sniffed_opt}; use std::borrow::ToOwned; use std::boxed::FnBox; use std::collections::HashSet; @@ -59,8 +59,11 @@ pub fn factory(user_agent: String, cookie_jar: Arc<RwLock<CookieStorage>>, devtools_chan: Option<Sender<DevtoolsControlMsg>>, connector: Arc<Pool<Connector>>) - -> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> { - box move |load_data: LoadData, senders, classifier| { + -> Box<FnBox(LoadData, + LoadConsumer, + Arc<MIMEClassifier>, + CancellationListener) + Send> { + box move |load_data: LoadData, senders, classifier, cancel_listener| { spawn_named(format!("http_loader for {}", load_data.url.serialize()), move || { load_for_consumer(load_data, senders, @@ -69,6 +72,7 @@ pub fn factory(user_agent: String, hsts_list, cookie_jar, devtools_chan, + cancel_listener, user_agent) }) } @@ -104,6 +108,7 @@ fn load_for_consumer(load_data: LoadData, hsts_list: Arc<RwLock<HSTSList>>, cookie_jar: Arc<RwLock<CookieStorage>>, devtools_chan: Option<Sender<DevtoolsControlMsg>>, + cancel_listener: CancellationListener, user_agent: String) { let factory = NetworkHttpRequestFactory { @@ -132,13 +137,12 @@ fn load_for_consumer(load_data: LoadData, image.push("badcert.html"); let load_data = LoadData::new(Url::from_file_path(&*image).unwrap(), None); - file_loader::factory(load_data, start_chan, classifier) - + file_loader::factory(load_data, start_chan, classifier, cancel_listener) } Err(LoadError::ConnectionAborted(_)) => unreachable!(), Ok(mut load_response) => { let metadata = load_response.metadata.clone(); - send_data(&mut load_response, start_chan, metadata, classifier) + send_data(&mut load_response, start_chan, metadata, classifier, cancel_listener) } } } @@ -717,7 +721,8 @@ pub fn load<A>(load_data: LoadData, fn send_data<R: Read>(reader: &mut R, start_chan: LoadConsumer, metadata: Metadata, - classifier: Arc<MIMEClassifier>) { + classifier: Arc<MIMEClassifier>, + cancel_listener: CancellationListener) { let (progress_chan, mut chunk) = { let buf = match read_block(reader) { Ok(ReadResult::Payload(buf)) => buf, @@ -731,6 +736,11 @@ fn send_data<R: Read>(reader: &mut R, }; loop { + if cancel_listener.is_cancelled() { + let _ = progress_chan.send(Done(Err("load cancelled".to_owned()))); + return; + } + if progress_chan.send(Payload(chunk)).is_err() { // The send errors when the receiver is out of scope, // which will happen if the fetch has timed out (or has been aborted) diff --git a/components/net/image_cache_task.rs b/components/net/image_cache_task.rs index a8df014b2dd..9739e89ddfc 100644 --- a/components/net/image_cache_task.rs +++ b/components/net/image_cache_task.rs @@ -429,7 +429,8 @@ impl ImageCache { sender: action_sender, }; let msg = ControlMsg::Load(load_data, - LoadConsumer::Listener(response_target)); + LoadConsumer::Listener(response_target), + None); let progress_sender = self.progress_sender.clone(); ROUTER.add_route(action_receiver.to_opaque(), box move |message| { let action: ResponseAction = message.to().unwrap(); diff --git a/components/net/resource_task.rs b/components/net/resource_task.rs index 529f79b448e..31993b8122a 100644 --- a/components/net/resource_task.rs +++ b/components/net/resource_task.rs @@ -19,10 +19,12 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use mime_classifier::{ApacheBugFlag, MIMEClassifier, NoSniffFlag}; use net_traits::ProgressMsg::Done; use net_traits::{AsyncResponseTarget, Metadata, ProgressMsg, ResourceTask, ResponseAction}; -use net_traits::{ControlMsg, CookieSource, LoadConsumer, LoadData, LoadResponse}; +use net_traits::{ControlMsg, CookieSource, LoadConsumer, LoadData, LoadResponse, ResourceId}; use std::borrow::ToOwned; use std::boxed::FnBox; -use std::sync::mpsc::{Sender, channel}; +use std::cell::Cell; +use std::collections::HashMap; +use std::sync::mpsc::{Receiver, Sender, channel}; use std::sync::{Arc, RwLock}; use url::Url; use util::opts; @@ -146,6 +148,7 @@ pub fn new_resource_task(user_agent: String, }; let (setup_chan, setup_port) = ipc::channel().unwrap(); + let setup_chan_clone = setup_chan.clone(); spawn_named("ResourceManager".to_owned(), move || { let resource_manager = ResourceManager::new( user_agent, hsts_preload, devtools_chan @@ -155,8 +158,7 @@ pub fn new_resource_task(user_agent: String, from_client: setup_port, resource_manager: resource_manager }; - - channel_manager.start(); + channel_manager.start(setup_chan_clone); }); setup_chan } @@ -167,28 +169,88 @@ struct ResourceChannelManager { } impl ResourceChannelManager { - fn start(&mut self) { + fn start(&mut self, control_sender: ResourceTask) { loop { match self.from_client.recv().unwrap() { - ControlMsg::Load(load_data, consumer) => { - self.resource_manager.load(load_data, consumer) - } - ControlMsg::SetCookiesForUrl(request, cookie_list, source) => { - self.resource_manager.set_cookies_for_url(request, cookie_list, source) - } + ControlMsg::Load(load_data, consumer, id_sender) => + self.resource_manager.load(load_data, consumer, id_sender, control_sender.clone()), + ControlMsg::SetCookiesForUrl(request, cookie_list, source) => + self.resource_manager.set_cookies_for_url(request, cookie_list, source), ControlMsg::GetCookiesForUrl(url, consumer, source) => { let cookie_jar = &self.resource_manager.cookie_storage; let mut cookie_jar = cookie_jar.write().unwrap(); consumer.send(cookie_jar.cookies_for_url(&url, source)).unwrap(); } - ControlMsg::Exit => { - break + ControlMsg::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); + } + ControlMsg::Synchronize(sender) => { + let _ = sender.send(()); } + ControlMsg::Exit => break, } } } } +/// The optional resources required by the `CancellationListener` +pub struct CancellableResource { + /// The receiver which receives a message on load cancellation + cancel_receiver: Receiver<()>, + /// The `CancellationListener` is unique to this `ResourceId` + resource_id: ResourceId, + /// If we haven't initiated any cancel requests, then the loaders ask + /// the listener to remove the `ResourceId` in the `HashMap` of + /// `ResourceManager` once they finish loading + resource_task: ResourceTask, +} + +/// A listener which is basically a wrapped optional receiver which looks +/// for the load cancellation message. Some of the loading processes always keep +/// an eye out for this message and stop loading stuff once they receive it. +pub struct CancellationListener { + /// We'll be needing the resources only if we plan to cancel it + cancel_resource: Option<CancellableResource>, + /// This lets us know whether the request has already been cancelled + cancel_status: Cell<bool>, +} + +impl CancellationListener { + pub fn new(resources: Option<CancellableResource>) -> CancellationListener { + CancellationListener { + cancel_resource: resources, + cancel_status: Cell::new(false), + } + } + + pub fn is_cancelled(&self) -> bool { + match self.cancel_resource { + Some(ref resource) => { + match resource.cancel_receiver.try_recv() { + Ok(_) => { + self.cancel_status.set(true); + true + }, + Err(_) => self.cancel_status.get(), + } + }, + None => false, // channel doesn't exist! + } + } +} + +impl Drop for CancellationListener { + fn drop(&mut self) { + if let Some(ref resource) = self.cancel_resource { + // Ensure that the resource manager stops tracking this request now that it's terminated. + let _ = resource.resource_task.send(ControlMsg::Cancel(resource.resource_id)); + } + } +} + pub struct ResourceManager { user_agent: String, cookie_storage: Arc<RwLock<CookieStorage>>, @@ -196,6 +258,8 @@ pub struct ResourceManager { devtools_chan: Option<Sender<DevtoolsControlMsg>>, hsts_list: Arc<RwLock<HSTSList>>, connector: Arc<Pool<Connector>>, + cancel_load_map: HashMap<ResourceId, Sender<()>>, + next_resource_id: ResourceId, } impl ResourceManager { @@ -209,11 +273,11 @@ impl ResourceManager { devtools_chan: devtools_channel, hsts_list: Arc::new(RwLock::new(hsts_list)), connector: create_http_connector(), + cancel_load_map: HashMap::new(), + next_resource_id: ResourceId(0), } } -} -impl ResourceManager { fn set_cookies_for_url(&mut self, request: Url, cookie_list: String, source: CookieSource) { let header = Header::parse_header(&[cookie_list.into_bytes()]); if let Ok(SetCookie(cookies)) = header { @@ -227,15 +291,36 @@ impl ResourceManager { } } - fn load(&mut self, load_data: LoadData, consumer: LoadConsumer) { + fn load(&mut self, + load_data: LoadData, + consumer: LoadConsumer, + id_sender: Option<IpcSender<ResourceId>>, + resource_task: ResourceTask) { - fn from_factory(factory: fn(LoadData, LoadConsumer, Arc<MIMEClassifier>)) - -> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> { - box move |load_data, senders, classifier| { - factory(load_data, senders, classifier) + fn from_factory(factory: fn(LoadData, LoadConsumer, Arc<MIMEClassifier>, CancellationListener)) + -> Box<FnBox(LoadData, + LoadConsumer, + Arc<MIMEClassifier>, + CancellationListener) + Send> { + box move |load_data, senders, classifier, cancel_listener| { + factory(load_data, senders, classifier, cancel_listener) } } + let cancel_resource = id_sender.map(|sender| { + let current_res_id = self.next_resource_id; + let _ = sender.send(current_res_id); + let (cancel_sender, cancel_receiver) = channel(); + self.cancel_load_map.insert(current_res_id, cancel_sender); + self.next_resource_id.0 += 1; + CancellableResource { + cancel_receiver: cancel_receiver, + resource_id: current_res_id, + resource_task: resource_task, + } + }); + + let cancel_listener = CancellationListener::new(cancel_resource); let loader = match &*load_data.url.scheme { "file" => from_factory(file_loader::factory), "http" | "https" | "view-source" => @@ -254,6 +339,9 @@ impl ResourceManager { }; debug!("resource_task: loading url: {}", load_data.url.serialize()); - loader.call_box((load_data, consumer, self.mime_classifier.clone())); + loader.call_box((load_data, + consumer, + self.mime_classifier.clone(), + cancel_listener)); } } diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index de59bf654d7..f5e70489fd4 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -227,12 +227,17 @@ pub enum IncludeSubdomains { #[derive(Deserialize, Serialize)] pub enum ControlMsg { /// Request the data associated with a particular URL - Load(LoadData, LoadConsumer), + Load(LoadData, LoadConsumer, Option<IpcSender<ResourceId>>), /// Store a set of cookies for a given originating URL SetCookiesForUrl(Url, String, CookieSource), /// Retrieve the stored cookies for a given URL GetCookiesForUrl(Url, IpcSender<Option<String>>, CookieSource), - Exit + /// Cancel a network request corresponding to a given `ResourceId` + Cancel(ResourceId), + /// Synchronization message solely for knowing the state of the ResourceChannelManager loop + Synchronize(IpcSender<()>), + /// Break the load handler loop and exit + Exit, } /// Initialized but unsent request. Encapsulates everything necessary to instruct @@ -279,7 +284,7 @@ impl PendingAsyncLoad { self.guard.neuter(); let load_data = LoadData::new(self.url, self.pipeline); let consumer = LoadConsumer::Listener(listener); - self.resource_task.send(ControlMsg::Load(load_data, consumer)).unwrap(); + self.resource_task.send(ControlMsg::Load(load_data, consumer, None)).unwrap(); } } @@ -377,7 +382,7 @@ pub fn load_whole_resource(resource_task: &ResourceTask, url: Url, pipeline_id: -> Result<(Metadata, Vec<u8>), String> { let (start_chan, start_port) = ipc::channel().unwrap(); resource_task.send(ControlMsg::Load(LoadData::new(url, pipeline_id), - LoadConsumer::Channel(start_chan))).unwrap(); + LoadConsumer::Channel(start_chan), None)).unwrap(); let response = start_port.recv().unwrap(); let mut buf = vec!(); @@ -389,3 +394,7 @@ pub fn load_whole_resource(resource_task: &ResourceTask, url: Url, pipeline_id: } } } + +/// An unique identifier to keep track of each load message in the resource handler +#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] +pub struct ResourceId(pub u32); diff --git a/components/plugins/reflector.rs b/components/plugins/reflector.rs index 027f2ecf172..84b960168d0 100644 --- a/components/plugins/reflector.rs +++ b/components/plugins/reflector.rs @@ -48,6 +48,19 @@ pub fn expand_reflector(cx: &mut ExtCtxt, span: Span, _: &MetaItem, annotatable: impl_item.map(|it| push(Annotatable::Item(it))) } }; + + let impl_item = quote_item!(cx, + impl ::js::conversions::ToJSValConvertible for $struct_name { + #[allow(unsafe_code)] + unsafe fn to_jsval(&self, + cx: *mut ::js::jsapi::JSContext, + rval: ::js::jsapi::MutableHandleValue) { + let object = ::dom::bindings::reflector::Reflectable::reflector(self).get_jsobject(); + object.to_jsval(cx, rval) + } + } + ); + impl_item.map(|it| push(Annotatable::Item(it))); } else { cx.span_err(span, "#[dom_struct] seems to have been applied to a non-struct"); } diff --git a/components/profile/heartbeats.rs b/components/profile/heartbeats.rs index 0101f62e25b..c87e9f542a8 100644 --- a/components/profile/heartbeats.rs +++ b/components/profile/heartbeats.rs @@ -52,7 +52,6 @@ pub fn init() { maybe_create_heartbeat(&mut hbs, ProfilerCategory::ScriptSetViewport); maybe_create_heartbeat(&mut hbs, ProfilerCategory::ScriptWebSocketEvent); maybe_create_heartbeat(&mut hbs, ProfilerCategory::ScriptWorkerEvent); - maybe_create_heartbeat(&mut hbs, ProfilerCategory::ScriptXhrEvent); maybe_create_heartbeat(&mut hbs, ProfilerCategory::ApplicationHeartbeat); unsafe { HBS = Some(mem::transmute(Box::new(hbs))); diff --git a/components/profile/time.rs b/components/profile/time.rs index aa3fd79199a..0de493368af 100644 --- a/components/profile/time.rs +++ b/components/profile/time.rs @@ -102,7 +102,6 @@ impl Formattable for ProfilerCategory { ProfilerCategory::ScriptTimerEvent => "Script Timer Event", ProfilerCategory::ScriptWebSocketEvent => "Script Web Socket Event", ProfilerCategory::ScriptWorkerEvent => "Script Worker Event", - ProfilerCategory::ScriptXhrEvent => "Script Xhr Event", ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat", }; format!("{}{}", padding, name) diff --git a/components/profile_traits/time.rs b/components/profile_traits/time.rs index 9fa31d69892..5ebfcc18e46 100644 --- a/components/profile_traits/time.rs +++ b/components/profile_traits/time.rs @@ -71,7 +71,6 @@ pub enum ProfilerCategory { ScriptTimerEvent, ScriptWebSocketEvent, ScriptWorkerEvent, - ScriptXhrEvent, ApplicationHeartbeat, } diff --git a/components/script/devtools.rs b/components/script/devtools.rs index b12619b51b0..3d1bc95c2de 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -28,33 +28,37 @@ use uuid::Uuid; #[allow(unsafe_code)] pub fn handle_evaluate_js(global: &GlobalRef, eval: String, reply: IpcSender<EvaluateJSReply>) { - let cx = global.get_cx(); - let mut rval = RootedValue::new(cx, UndefinedValue()); - global.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); - - reply.send(if rval.ptr.is_undefined() { - EvaluateJSReply::VoidValue - } else if rval.ptr.is_boolean() { - EvaluateJSReply::BooleanValue(rval.ptr.to_boolean()) - } else if rval.ptr.is_double() || rval.ptr.is_int32() { - EvaluateJSReply::NumberValue( - FromJSValConvertible::from_jsval(cx, rval.handle(), ()).unwrap()) - } else if rval.ptr.is_string() { - EvaluateJSReply::StringValue(jsstring_to_str(cx, rval.ptr.to_string()).0) - } else if rval.ptr.is_null() { - EvaluateJSReply::NullValue - } else { - assert!(rval.ptr.is_object()); - - let obj = RootedObject::new(cx, rval.ptr.to_object()); - let class_name = unsafe { CStr::from_ptr(ObjectClassName(cx, obj.handle())) }; - let class_name = str::from_utf8(class_name.to_bytes()).unwrap(); - - EvaluateJSReply::ActorValue { - class: class_name.to_owned(), - uuid: Uuid::new_v4().to_string(), + // global.get_cx() returns a valid `JSContext` pointer, so this is safe. + let result = unsafe { + let cx = global.get_cx(); + let mut rval = RootedValue::new(cx, UndefinedValue()); + global.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); + + if rval.ptr.is_undefined() { + EvaluateJSReply::VoidValue + } else if rval.ptr.is_boolean() { + EvaluateJSReply::BooleanValue(rval.ptr.to_boolean()) + } else if rval.ptr.is_double() || rval.ptr.is_int32() { + EvaluateJSReply::NumberValue( + FromJSValConvertible::from_jsval(cx, rval.handle(), ()).unwrap()) + } else if rval.ptr.is_string() { + EvaluateJSReply::StringValue(jsstring_to_str(cx, rval.ptr.to_string()).0) + } else if rval.ptr.is_null() { + EvaluateJSReply::NullValue + } else { + assert!(rval.ptr.is_object()); + + let obj = RootedObject::new(cx, rval.ptr.to_object()); + let class_name = CStr::from_ptr(ObjectClassName(cx, obj.handle())); + let class_name = str::from_utf8(class_name.to_bytes()).unwrap(); + + EvaluateJSReply::ActorValue { + class: class_name.to_owned(), + uuid: Uuid::new_v4().to_string(), + } } - }).unwrap(); + }; + reply.send(result).unwrap(); } pub fn handle_get_root_node(page: &Rc<Page>, pipeline: PipelineId, reply: IpcSender<NodeInfo>) { diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 5ac6c455e39..14a26268efd 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -888,21 +888,16 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, else: handleInvalidEnumValueCode = "return true;" - transmute = "mem::transmute(index)" - if isMember == 'Dictionary': - transmute = 'unsafe { ' + transmute + ' }' - template = ( "match find_enum_string_index(cx, ${val}, %(values)s) {\n" " Err(_) => { %(exceptionCode)s },\n" " Ok(None) => { %(handleInvalidEnumValueCode)s },\n" " Ok(Some(index)) => {\n" " //XXXjdm need some range checks up in here.\n" - " %(transmute)s\n" + " mem::transmute(index)\n" " },\n" "}" % {"values": enum + "Values::strings", "exceptionCode": exceptionCode, - "transmute": transmute, "handleInvalidEnumValueCode": handleInvalidEnumValueCode}) if defaultValue is not None: @@ -1295,7 +1290,12 @@ def getRetvalDeclarationForType(returnType, descriptorProvider): if returnType.isObject() or returnType.isSpiderMonkeyInterface(): return CGGeneric("*mut JSObject") if returnType.isSequence(): - raise TypeError("We don't support sequence return values") + inner = returnType.unroll() + result = getRetvalDeclarationForType(inner, descriptorProvider) + result = CGWrapper(result, pre="Vec<", post=">") + if returnType.nullable(): + result = CGWrapper(result, pre="Option<", post=">") + return result if returnType.isDictionary(): nullable = returnType.nullable() dictName = returnType.inner.name if nullable else returnType.name @@ -3413,7 +3413,7 @@ pub const strings: &'static [&'static str] = &[ ]; impl ToJSValConvertible for super::%s { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { strings[*self as usize].to_jsval(cx, rval); } } @@ -3534,7 +3534,7 @@ pub enum %s { } impl ToJSValConvertible for %s { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { match *self { %s } @@ -3648,8 +3648,8 @@ class CGUnionConversionStruct(CGThing): "Err(())" % ", ".join(names))) method = CGWrapper( CGIndenter(CGList(conversions, "\n\n")), - pre="fn from_jsval(cx: *mut JSContext,\n" - " value: HandleValue, _option: ()) -> Result<%s, ()> {\n" % self.type, + pre="unsafe fn from_jsval(cx: *mut JSContext,\n" + " value: HandleValue, _option: ()) -> Result<%s, ()> {\n" % self.type, post="\n}") return CGWrapper( CGIndenter(CGList([ @@ -3666,7 +3666,7 @@ class CGUnionConversionStruct(CGThing): return CGWrapper( CGIndenter(jsConversion, 4), - pre="fn TryConvertTo%s(cx: *mut JSContext, value: HandleValue) -> %s {\n" % (t.name, returnType), + pre="unsafe fn TryConvertTo%s(cx: *mut JSContext, value: HandleValue) -> %s {\n" % (t.name, returnType), post="\n}") def define(self): @@ -4947,10 +4947,10 @@ class CGDictionary(CGThing): return string.Template( "impl ${selfName} {\n" - " pub fn empty(cx: *mut JSContext) -> ${selfName} {\n" + " pub unsafe fn empty(cx: *mut JSContext) -> ${selfName} {\n" " ${selfName}::new(cx, HandleValue::null()).unwrap()\n" " }\n" - " pub fn new(cx: *mut JSContext, val: HandleValue) -> Result<${selfName}, ()> {\n" + " pub unsafe fn new(cx: *mut JSContext, val: HandleValue) -> Result<${selfName}, ()> {\n" " let object = if val.get().is_null_or_undefined() {\n" " RootedObject::new(cx, ptr::null_mut())\n" " } else if val.get().is_object() {\n" @@ -4967,8 +4967,8 @@ class CGDictionary(CGThing): "}\n" "\n" "impl ToJSValConvertible for ${selfName} {\n" - " fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {\n" - " let obj = unsafe { RootedObject::new(cx, JS_NewObject(cx, ptr::null())) };\n" + " unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {\n" + " let obj = RootedObject::new(cx, JS_NewObject(cx, ptr::null()));\n" "${insertMembers}" " rval.set(ObjectOrNullValue(obj.ptr))\n" " }\n" @@ -5154,6 +5154,7 @@ class CGBindingRoot(CGThing): 'js::{JSCLASS_RESERVED_SLOTS_MASK}', 'js::{JSPROP_ENUMERATE, JSPROP_SHARED}', 'js::{JSITER_OWNONLY, JSITER_HIDDEN, JSITER_SYMBOLS}', + 'js::error::throw_type_error', 'js::jsapi::{JS_CallFunctionValue, JS_GetClass, JS_GetGlobalForObject}', 'js::jsapi::{JS_GetObjectPrototype, JS_GetProperty, JS_GetPropertyById}', 'js::jsapi::{JS_GetPropertyDescriptorById, JS_GetReservedSlot}', @@ -5217,7 +5218,6 @@ class CGBindingRoot(CGThing): 'dom::bindings::error::{Fallible, Error, ErrorResult}', 'dom::bindings::error::Error::JSFailed', 'dom::bindings::error::throw_dom_exception', - 'dom::bindings::error::throw_type_error', 'dom::bindings::proxyhandler', 'dom::bindings::proxyhandler::{fill_property_descriptor, get_expando_object}', 'dom::bindings::proxyhandler::{get_property_descriptor}', @@ -5459,7 +5459,7 @@ impl CallbackContainer for ${type} { } impl ToJSValConvertible for ${type} { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { self.callback().to_jsval(cx, rval); } }\ @@ -5563,10 +5563,10 @@ class CallbackMember(CGNativeMember): "${convertArgs}" "${doCall}" "${returnResult}").substitute(replacements) - return CGList([ + return CGWrapper(CGIndenter(CGList([ CGGeneric(pre), CGGeneric(body), - ], "\n").define() + ], "\n"), 4), pre="unsafe {\n", post="\n}").define() def getResultConversion(self): replacements = { @@ -5709,15 +5709,13 @@ class CallbackMethod(CallbackMember): replacements["argc"] = "0" return string.Template( "${getCallable}" - "let ok = unsafe {\n" - " let rootedThis = RootedObject::new(cx, ${thisObj});\n" - " JS_CallFunctionValue(\n" - " cx, rootedThis.handle(), callable.handle(),\n" - " &HandleValueArray {\n" - " length_: ${argc} as ::libc::size_t,\n" - " elements_: ${argv}\n" - " }, rval.handle_mut())\n" - "};\n" + "let rootedThis = RootedObject::new(cx, ${thisObj});\n" + "let ok = JS_CallFunctionValue(\n" + " cx, rootedThis.handle(), callable.handle(),\n" + " &HandleValueArray {\n" + " length_: ${argc} as ::libc::size_t,\n" + " elements_: ${argv}\n" + " }, rval.handle_mut());\n" "if !ok {\n" " return Err(JSFailed);\n" "}\n").substitute(replacements) @@ -5732,7 +5730,7 @@ class CallCallback(CallbackMethod): return "aThisObj.get()" def getCallableDecl(self): - return "let callable = RootedValue::new(cx, ObjectValue(unsafe {&*self.parent.callback()}));\n" + return "let callable = RootedValue::new(cx, ObjectValue(&*self.parent.callback()));\n" class CallbackOperationBase(CallbackMethod): @@ -5762,11 +5760,11 @@ class CallbackOperationBase(CallbackMethod): if not self.singleOperation: return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp return ( - 'let isCallable = unsafe { IsCallable(self.parent.callback()) };\n' + 'let isCallable = IsCallable(self.parent.callback());\n' 'let callable =\n' + CGIndenter( CGIfElseWrapper('isCallable', - CGGeneric('unsafe { RootedValue::new(cx, ObjectValue(&*self.parent.callback())) }'), + CGGeneric('RootedValue::new(cx, ObjectValue(&*self.parent.callback()))'), CGGeneric(getCallableFromProp))).define() + ';\n') diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index 45279e7a966..dcc1f164d25 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -32,7 +32,6 @@ //! | sequences | `Vec<T>` | //! | union types | `T` | -use dom::bindings::error::throw_type_error; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::bindings::num::Finite; @@ -40,24 +39,23 @@ use dom::bindings::reflector::{Reflectable, Reflector}; use dom::bindings::str::{ByteString, USVString}; use dom::bindings::utils::DOMClass; use js; -use js::glue::{GetProxyPrivate, IsWrapper, RUST_JS_NumberValue}; +pub use js::conversions::{FromJSValConvertible, ToJSValConvertible, ConversionBehavior}; +use js::error::throw_type_error; +use js::glue::{GetProxyPrivate, IsWrapper}; use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_STRING, UnwrapObject}; use js::jsapi::{HandleId, HandleObject, HandleValue, JS_GetClass}; -use js::jsapi::{JSClass, JSContext, JSObject, JSString, MutableHandleValue}; +use js::jsapi::{JSClass, JSContext, JSObject, MutableHandleValue}; use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetReservedSlot}; use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_NewStringCopyN}; -use js::jsapi::{JS_NewUCStringCopyN, JS_StringHasLatin1Chars, JS_WrapValue}; -use js::jsval::{BooleanValue, Int32Value, NullValue, UInt32Value, UndefinedValue}; -use js::jsval::{JSVal, ObjectOrNullValue, ObjectValue, StringValue}; -use js::rust::{ToBoolean, ToNumber, ToString, ToUint16}; -use js::rust::{ToInt32, ToUint32}; -use js::rust::{ToInt64, ToUint64}; +use js::jsapi::{JS_StringHasLatin1Chars, JS_WrapValue}; +use js::jsval::{ObjectValue, StringValue}; +use js::rust::ToString; use libc; use num::Float; -use num::traits::{Bounded, Zero}; -use std::rc::Rc; -use std::{char, ptr, slice}; -use util::str::DOMString; +use std::{ptr, slice}; +use util::str::{DOMString}; +pub use util::str::{StringificationBehavior, jsstring_to_str}; + trait As<O>: Copy { fn cast(self) -> O; @@ -110,290 +108,9 @@ pub trait IDLInterface { #[rustc_on_unimplemented = "The IDL interface `{Self}` is not derived from `{T}`."] pub trait DerivedFrom<T: Castable>: Castable {} -/// A trait to convert Rust types to `JSVal`s. -pub trait ToJSValConvertible { - /// Convert `self` to a `JSVal`. JSAPI failure causes a task failure. - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue); -} - -/// A trait to convert `JSVal`s to Rust types. -pub trait FromJSValConvertible: Sized { - /// Optional configurable behaviour switch; use () for no configuration. - type Config; - /// Convert `val` to type `Self`. - /// Optional configuration of type `T` can be passed as the `option` - /// argument. - /// If it returns `Err(())`, a JSAPI exception is pending. - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: Self::Config) -> Result<Self, ()>; -} - -/// Behavior for converting out-of-range integers. -#[derive(PartialEq, Eq)] -pub enum ConversionBehavior { - /// Wrap into the integer's range. - Default, - /// Throw an exception. - EnforceRange, - /// Clamp into the integer's range. - Clamp -} - -/// Try to cast the number to a smaller type, but -/// if it doesn't fit, it will return an error. -fn enforce_range<D>(cx: *mut JSContext, d: f64) -> Result<D, ()> - where D: Bounded + As<f64>, - f64: As<D> -{ - if d.is_infinite() { - throw_type_error(cx, "value out of range in an EnforceRange argument"); - return Err(()); - } - - let rounded = d.round(); - if D::min_value().cast() <= rounded && rounded <= D::max_value().cast() { - Ok(rounded.cast()) - } else { - throw_type_error(cx, "value out of range in an EnforceRange argument"); - Err(()) - } -} - -/// Try to cast the number to a smaller type, but if it doesn't fit, -/// round it to the MAX or MIN of the source type before casting it to -/// the destination type. -fn clamp_to<D>(d: f64) -> D - where D: Bounded + As<f64> + Zero, - f64: As<D> -{ - if d.is_nan() { - D::zero() - } else if d > D::max_value().cast() { - D::max_value() - } else if d < D::min_value().cast() { - D::min_value() - } else { - d.cast() - } -} - -//http://heycam.github.io/webidl/#es-void -impl ToJSValConvertible for () { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(UndefinedValue()); - } -} - -impl ToJSValConvertible for JSVal { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(*self); - if unsafe { !JS_WrapValue(cx, rval) } { - panic!("JS_WrapValue failed."); - } - } -} - -impl ToJSValConvertible for HandleValue { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(self.get()); - if unsafe { !JS_WrapValue(cx, rval) } { - panic!("JS_WrapValue failed."); - } - } -} - -#[inline] -unsafe fn convert_int_from_jsval<T, M>(cx: *mut JSContext, value: HandleValue, - option: ConversionBehavior, - convert_fn: unsafe fn(*mut JSContext, HandleValue) -> Result<M, ()>) - -> Result<T, ()> - where T: Bounded + Zero + As<f64>, - M: Zero + As<T>, - f64: As<T> -{ - match option { - ConversionBehavior::Default => Ok(try!(convert_fn(cx, value)).cast()), - ConversionBehavior::EnforceRange => enforce_range(cx, try!(ToNumber(cx, value))), - ConversionBehavior::Clamp => Ok(clamp_to(try!(ToNumber(cx, value)))), - } -} - -//http://heycam.github.io/webidl/#es-boolean -impl ToJSValConvertible for bool { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(BooleanValue(*self)); - } -} - -//http://heycam.github.io/webidl/#es-boolean -impl FromJSValConvertible for bool { - type Config = (); - fn from_jsval(_cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<bool, ()> { - Ok(unsafe { ToBoolean(val) }) - } -} - -//http://heycam.github.io/webidl/#es-byte -impl ToJSValConvertible for i8 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(Int32Value(*self as i32)); - } -} - -//http://heycam.github.io/webidl/#es-byte -impl FromJSValConvertible for i8 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<i8, ()> { - unsafe { convert_int_from_jsval(cx, val, option, ToInt32) } - } -} - -//http://heycam.github.io/webidl/#es-octet -impl ToJSValConvertible for u8 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(Int32Value(*self as i32)); - } -} - -//http://heycam.github.io/webidl/#es-octet -impl FromJSValConvertible for u8 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<u8, ()> { - unsafe { convert_int_from_jsval(cx, val, option, ToInt32) } - } -} - -//http://heycam.github.io/webidl/#es-short -impl ToJSValConvertible for i16 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(Int32Value(*self as i32)); - } -} - -//http://heycam.github.io/webidl/#es-short -impl FromJSValConvertible for i16 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<i16, ()> { - unsafe { convert_int_from_jsval(cx, val, option, ToInt32) } - } -} - -//http://heycam.github.io/webidl/#es-unsigned-short -impl ToJSValConvertible for u16 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(Int32Value(*self as i32)); - } -} - -//http://heycam.github.io/webidl/#es-unsigned-short -impl FromJSValConvertible for u16 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<u16, ()> { - unsafe { convert_int_from_jsval(cx, val, option, ToUint16) } - } -} - -//http://heycam.github.io/webidl/#es-long -impl ToJSValConvertible for i32 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(Int32Value(*self)); - } -} - -//http://heycam.github.io/webidl/#es-long -impl FromJSValConvertible for i32 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<i32, ()> { - unsafe { convert_int_from_jsval(cx, val, option, ToInt32) } - } -} - -//http://heycam.github.io/webidl/#es-unsigned-long -impl ToJSValConvertible for u32 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(UInt32Value(*self)); - } -} - -//http://heycam.github.io/webidl/#es-unsigned-long -impl FromJSValConvertible for u32 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<u32, ()> { - unsafe { convert_int_from_jsval(cx, val, option, ToUint32) } - } -} - -//http://heycam.github.io/webidl/#es-long-long -impl ToJSValConvertible for i64 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - unsafe { - rval.set(RUST_JS_NumberValue(*self as f64)); - } - } -} - -//http://heycam.github.io/webidl/#es-long-long -impl FromJSValConvertible for i64 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<i64, ()> { - unsafe { convert_int_from_jsval(cx, val, option, ToInt64) } - } -} - -//http://heycam.github.io/webidl/#es-unsigned-long-long -impl ToJSValConvertible for u64 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - unsafe { - rval.set(RUST_JS_NumberValue(*self as f64)); - } - } -} - -//http://heycam.github.io/webidl/#es-unsigned-long-long -impl FromJSValConvertible for u64 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<u64, ()> { - unsafe { convert_int_from_jsval(cx, val, option, ToUint64) } - } -} - -//http://heycam.github.io/webidl/#es-float -impl ToJSValConvertible for f32 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - unsafe { - rval.set(RUST_JS_NumberValue(*self as f64)); - } - } -} - -//http://heycam.github.io/webidl/#es-float -impl FromJSValConvertible for f32 { - type Config = (); - fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<f32, ()> { - let result = unsafe { ToNumber(cx, val) }; - result.map(|f| f as f32) - } -} - -//http://heycam.github.io/webidl/#es-double -impl ToJSValConvertible for f64 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - unsafe { - rval.set(RUST_JS_NumberValue(*self)); - } - } -} - -//http://heycam.github.io/webidl/#es-double -impl FromJSValConvertible for f64 { - type Config = (); - fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<f64, ()> { - unsafe { ToNumber(cx, val) } - } -} - impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> { #[inline] - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { let value = **self; value.to_jsval(cx, rval); } @@ -402,7 +119,7 @@ impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> { impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite<T> { type Config = (); - fn from_jsval(cx: *mut JSContext, value: HandleValue, option: ()) -> Result<Finite<T>, ()> { + unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, option: ()) -> Result<Finite<T>, ()> { let result = try!(FromJSValConvertible::from_jsval(cx, value, option)); match Finite::new(result) { Some(v) => Ok(v), @@ -414,97 +131,6 @@ impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite } } -impl ToJSValConvertible for str { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - let mut string_utf16: Vec<u16> = Vec::with_capacity(self.len()); - unsafe { - string_utf16.extend(self.utf16_units()); - let jsstr = JS_NewUCStringCopyN(cx, string_utf16.as_ptr(), - string_utf16.len() as libc::size_t); - if jsstr.is_null() { - panic!("JS_NewUCStringCopyN failed"); - } - rval.set(StringValue(&*jsstr)); - } - } -} - -//http://heycam.github.io/webidl/#es-DOMString -impl ToJSValConvertible for String { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - (**self).to_jsval(cx, rval); - } -} - -//http://heycam.github.io/webidl/#es-DOMString -impl ToJSValConvertible for DOMString { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - (**self).to_jsval(cx, rval); - } -} - -/// Behavior for stringification of `JSVal`s. -#[derive(PartialEq)] -pub enum StringificationBehavior { - /// Convert `null` to the string `"null"`. - Default, - /// Convert `null` to the empty string. - Empty, -} - -/// Convert the given `JSString` to a `DOMString`. Fails if the string does not -/// contain valid UTF-16. -pub fn jsstring_to_str(cx: *mut JSContext, s: *mut JSString) -> DOMString { - let mut length = 0; - let latin1 = unsafe { JS_StringHasLatin1Chars(s) }; - DOMString(if latin1 { - let chars = unsafe { - JS_GetLatin1StringCharsAndLength(cx, ptr::null(), s, &mut length) - }; - assert!(!chars.is_null()); - - let mut buf = String::with_capacity(length as usize); - for i in 0..(length as isize) { - unsafe { - buf.push(*chars.offset(i) as char); - } - } - buf - } else { - let chars = unsafe { - JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), s, &mut length) - }; - assert!(!chars.is_null()); - let potentially_ill_formed_utf16 = unsafe { - slice::from_raw_parts(chars as *const u16, length as usize) - }; - let mut s = String::with_capacity(length as usize); - for item in char::decode_utf16(potentially_ill_formed_utf16.iter().cloned()) { - match item { - Ok(c) => s.push(c), - Err(_) => { - // FIXME: Add more info like document URL in the message? - macro_rules! message { - () => { - "Found an unpaired surrogate in a DOM string. \ - If you see this in real web content, \ - please comment on https://github.com/servo/servo/issues/6564" - } - } - if ::util::opts::get().replace_surrogates { - error!(message!()); - s.push('\u{FFFD}'); - } else { - panic!(concat!(message!(), " Use `-Z replace-surrogates` \ - on the command line to make this non-fatal.")); - } - } - } - } - s - }) -} - /// Convert the given `jsid` to a `DOMString`. Fails if the `jsid` is not a /// string, or if the string does not contain valid UTF-16. pub fn jsid_to_str(cx: *mut JSContext, id: HandleId) -> DOMString { @@ -514,30 +140,9 @@ pub fn jsid_to_str(cx: *mut JSContext, id: HandleId) -> DOMString { } } -//http://heycam.github.io/webidl/#es-DOMString -impl FromJSValConvertible for DOMString { - type Config = StringificationBehavior; - fn from_jsval(cx: *mut JSContext, value: HandleValue, - null_behavior: StringificationBehavior) - -> Result<DOMString, ()> { - if null_behavior == StringificationBehavior::Empty && - value.get().is_null() { - Ok(DOMString::new()) - } else { - let jsstr = unsafe { ToString(cx, value) }; - if jsstr.is_null() { - debug!("ToString failed"); - Err(()) - } else { - Ok(jsstring_to_str(cx, jsstr)) - } - } - } -} - //http://heycam.github.io/webidl/#es-USVString impl ToJSValConvertible for USVString { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { self.0.to_jsval(cx, rval); } } @@ -545,89 +150,78 @@ impl ToJSValConvertible for USVString { //http://heycam.github.io/webidl/#es-USVString impl FromJSValConvertible for USVString { type Config = (); - fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) - -> Result<USVString, ()> { - let jsstr = unsafe { ToString(cx, value) }; + unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) + -> Result<USVString, ()> { + let jsstr = ToString(cx, value); if jsstr.is_null() { debug!("ToString failed"); return Err(()); } - let latin1 = unsafe { JS_StringHasLatin1Chars(jsstr) }; + let latin1 = JS_StringHasLatin1Chars(jsstr); if latin1 { return Ok(USVString(jsstring_to_str(cx, jsstr).0)); } - unsafe { - let mut length = 0; - let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr, &mut length); - assert!(!chars.is_null()); - let char_vec = slice::from_raw_parts(chars as *const u16, length as usize); - Ok(USVString(String::from_utf16_lossy(char_vec))) - } + let mut length = 0; + let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr, &mut length); + assert!(!chars.is_null()); + let char_vec = slice::from_raw_parts(chars as *const u16, length as usize); + Ok(USVString(String::from_utf16_lossy(char_vec))) } } //http://heycam.github.io/webidl/#es-ByteString impl ToJSValConvertible for ByteString { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - unsafe { - let jsstr = JS_NewStringCopyN(cx, self.as_ptr() as *const libc::c_char, - self.len() as libc::size_t); - if jsstr.is_null() { - panic!("JS_NewStringCopyN failed"); - } - rval.set(StringValue(&*jsstr)); + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + let jsstr = JS_NewStringCopyN(cx, self.as_ptr() as *const libc::c_char, + self.len() as libc::size_t); + if jsstr.is_null() { + panic!("JS_NewStringCopyN failed"); } + rval.set(StringValue(&*jsstr)); } } //http://heycam.github.io/webidl/#es-ByteString impl FromJSValConvertible for ByteString { type Config = (); - fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ()) -> Result<ByteString, ()> { - let string = unsafe { ToString(cx, value) }; + unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ()) -> Result<ByteString, ()> { + let string = ToString(cx, value); if string.is_null() { debug!("ToString failed"); return Err(()); } - let latin1 = unsafe { JS_StringHasLatin1Chars(string) }; + let latin1 = JS_StringHasLatin1Chars(string); if latin1 { let mut length = 0; - let chars = unsafe { - JS_GetLatin1StringCharsAndLength(cx, ptr::null(), - string, &mut length) - }; + let chars = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), + string, &mut length); assert!(!chars.is_null()); - let char_slice = unsafe { - slice::from_raw_parts(chars as *mut u8, length as usize) - }; - + let char_slice = slice::from_raw_parts(chars as *mut u8, length as usize); return Ok(ByteString::new(char_slice.to_vec())); } - unsafe { - let mut length = 0; - let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), string, &mut length); - let char_vec = slice::from_raw_parts(chars, length as usize); + let mut length = 0; + let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), string, &mut length); + let char_vec = slice::from_raw_parts(chars, length as usize); - if char_vec.iter().any(|&c| c > 0xFF) { - throw_type_error(cx, "Invalid ByteString"); - Err(()) - } else { - Ok(ByteString::new(char_vec.iter().map(|&c| c as u8).collect())) - } + if char_vec.iter().any(|&c| c > 0xFF) { + throw_type_error(cx, "Invalid ByteString"); + Err(()) + } else { + Ok(ByteString::new(char_vec.iter().map(|&c| c as u8).collect())) } } } impl ToJSValConvertible for Reflector { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { let obj = self.get_jsobject().get(); assert!(!obj.is_null()); - rval.set(ObjectValue(unsafe { &*obj })); - if unsafe { !JS_WrapValue(cx, rval) } { + rval.set(ObjectValue(&*obj)); + if !JS_WrapValue(cx, rval) { panic!("JS_WrapValue failed."); } } @@ -770,53 +364,7 @@ pub fn root_from_handleobject<T>(obj: HandleObject) -> Result<Root<T>, ()> } impl<T: Reflectable> ToJSValConvertible for Root<T> { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - self.reflector().to_jsval(cx, rval); - } -} - -impl<'a, T: Reflectable> ToJSValConvertible for &'a T { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { self.reflector().to_jsval(cx, rval); } } - -impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - match self { - &Some(ref value) => value.to_jsval(cx, rval), - &None => rval.set(NullValue()), - } - } -} - -impl<T: ToJSValConvertible> ToJSValConvertible for Option<Rc<T>> { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - match self { - &Some(ref value) => (**value).to_jsval(cx, rval), - &None => rval.set(NullValue()), - } - } -} - -impl<T: FromJSValConvertible> FromJSValConvertible for Option<T> { - type Config = T::Config; - fn from_jsval(cx: *mut JSContext, value: HandleValue, option: T::Config) -> Result<Option<T>, ()> { - if value.get().is_null_or_undefined() { - Ok(None) - } else { - let result: Result<T, ()> = FromJSValConvertible::from_jsval(cx, value, option); - result.map(Some) - } - } -} - -//http://heycam.github.io/webidl/#es-object -impl ToJSValConvertible for *mut JSObject { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(ObjectOrNullValue(*self)); - unsafe { - assert!(JS_WrapValue(cx, rval)); - } - } -} diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index c0f03ae3c60..95f22e693e5 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -8,15 +8,12 @@ use dom::bindings::codegen::PrototypeList::proto_id_to_name; use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::global::GlobalRef; use dom::domexception::{DOMErrorName, DOMException}; +use js::error::{throw_range_error, throw_type_error}; use js::jsapi::JSAutoCompartment; use js::jsapi::{JSContext, JSObject, RootedValue}; -use js::jsapi::{JSErrorFormatString, JSExnType, JS_ReportErrorNumber}; use js::jsapi::{JS_IsExceptionPending, JS_ReportPendingException, JS_SetPendingException}; use js::jsapi::{JS_RestoreFrameChain, JS_SaveFrameChain}; use js::jsval::UndefinedValue; -use libc; -use std::ffi::CString; -use std::{mem, ptr}; use util::mem::HeapSizeOf; /// DOM exceptions that can be thrown by a native DOM method. @@ -80,8 +77,7 @@ pub type Fallible<T> = Result<T, Error>; pub type ErrorResult = Fallible<()>; /// Set a pending exception for the given `result` on `cx`. -pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef, - result: Error) { +pub unsafe fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef, result: Error) { let code = match result { Error::IndexSize => DOMErrorName::IndexSizeError, Error::NotFound => DOMErrorName::NotFoundError, @@ -104,28 +100,26 @@ pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef, Error::QuotaExceeded => DOMErrorName::QuotaExceededError, Error::TypeMismatch => DOMErrorName::TypeMismatchError, Error::Type(message) => { - assert!(unsafe { !JS_IsExceptionPending(cx) }); + assert!(!JS_IsExceptionPending(cx)); throw_type_error(cx, &message); return; }, Error::Range(message) => { - assert!(unsafe { !JS_IsExceptionPending(cx) }); + assert!(!JS_IsExceptionPending(cx)); throw_range_error(cx, &message); return; }, Error::JSFailed => { - assert!(unsafe { JS_IsExceptionPending(cx) }); + assert!(JS_IsExceptionPending(cx)); return; } }; - assert!(unsafe { !JS_IsExceptionPending(cx) }); + assert!(!JS_IsExceptionPending(cx)); let exception = DOMException::new(global, code); let mut thrown = RootedValue::new(cx, UndefinedValue()); exception.to_jsval(cx, thrown.handle_mut()); - unsafe { - JS_SetPendingException(cx, thrown.handle()); - } + JS_SetPendingException(cx, thrown.handle()); } /// Report a pending exception, thereby clearing it. @@ -146,76 +140,17 @@ pub fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) { /// Throw an exception to signal that a `JSVal` can not be converted to any of /// the types in an IDL union type. -pub fn throw_not_in_union(cx: *mut JSContext, names: &'static str) { - assert!(unsafe { !JS_IsExceptionPending(cx) }); +pub unsafe fn throw_not_in_union(cx: *mut JSContext, names: &'static str) { + assert!(!JS_IsExceptionPending(cx)); let error = format!("argument could not be converted to any of: {}", names); throw_type_error(cx, &error); } /// Throw an exception to signal that a `JSObject` can not be converted to a /// given DOM type. -pub fn throw_invalid_this(cx: *mut JSContext, proto_id: u16) { - debug_assert!(unsafe { !JS_IsExceptionPending(cx) }); +pub unsafe fn throw_invalid_this(cx: *mut JSContext, proto_id: u16) { + debug_assert!(!JS_IsExceptionPending(cx)); let error = format!("\"this\" object does not implement interface {}.", proto_id_to_name(proto_id)); throw_type_error(cx, &error); } - -/// Format string used to throw javascript errors. -static ERROR_FORMAT_STRING_STRING: [libc::c_char; 4] = [ - '{' as libc::c_char, - '0' as libc::c_char, - '}' as libc::c_char, - 0 as libc::c_char, -]; - -/// Format string struct used to throw `TypeError`s. -static mut TYPE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString { - format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char, - argCount: 1, - exnType: JSExnType::JSEXN_TYPEERR as i16, -}; - -/// Format string struct used to throw `RangeError`s. -static mut RANGE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString { - format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char, - argCount: 1, - exnType: JSExnType::JSEXN_RANGEERR as i16, -}; - -/// Callback used to throw javascript errors. -/// See throw_js_error for info about error_number. -unsafe extern fn get_error_message(_user_ref: *mut libc::c_void, - error_number: libc::c_uint) - -> *const JSErrorFormatString -{ - let num: JSExnType = mem::transmute(error_number); - match num { - JSExnType::JSEXN_TYPEERR => &TYPE_ERROR_FORMAT_STRING as *const JSErrorFormatString, - JSExnType::JSEXN_RANGEERR => &RANGE_ERROR_FORMAT_STRING as *const JSErrorFormatString, - _ => panic!("Bad js error number given to get_error_message: {}", error_number) - } -} - -/// Helper fn to throw a javascript error with the given message and number. -/// Reuse the jsapi error codes to distinguish the error_number -/// passed back to the get_error_message callback. -/// c_uint is u32, so this cast is safe, as is casting to/from i32 from there. -fn throw_js_error(cx: *mut JSContext, error: &str, error_number: u32) { - let error = CString::new(error).unwrap(); - unsafe { - JS_ReportErrorNumber(cx, - Some(get_error_message), - ptr::null_mut(), error_number, error.as_ptr()); - } -} - -/// Throw a `TypeError` with the given message. -pub fn throw_type_error(cx: *mut JSContext, error: &str) { - throw_js_error(cx, error, JSExnType::JSEXN_TYPEERR as u32); -} - -/// Throw a `RangeError` with the given message. -pub fn throw_range_error(cx: *mut JSContext, error: &str) { - throw_js_error(cx, error, JSExnType::JSEXN_RANGEERR as u32); -} diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 45e7db81d08..774738a3030 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -6,14 +6,15 @@ use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH; -use dom::bindings::conversions::{DOM_OBJECT_SLOT, is_dom_class, jsstring_to_str}; +use dom::bindings::conversions::{DOM_OBJECT_SLOT, is_dom_class}; use dom::bindings::conversions::{private_from_proto_check, root_from_handleobject}; -use dom::bindings::error::{throw_invalid_this, throw_type_error}; +use dom::bindings::error::throw_invalid_this; use dom::bindings::inheritance::TopTypeId; use dom::bindings::trace::trace_object; use dom::browsercontext; use dom::window; use js; +use js::error::throw_type_error; use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper}; use js::glue::{GetCrossCompartmentWrapper, WrapperNew}; use js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT}; @@ -49,6 +50,7 @@ use std::default::Default; use std::ffi::CString; use std::ptr; use util::mem::HeapSizeOf; +use util::str::jsstring_to_str; /// Proxy handler for a WindowProxy. #[allow(raw_pointer_derive)] @@ -440,11 +442,11 @@ pub fn get_array_index_from_id(_cx: *mut JSContext, id: HandleId) -> Option<u32> /// Find the index of a string given by `v` in `values`. /// Returns `Err(())` on JSAPI failure (there is a pending exception), and /// `Ok(None)` if there was no matching string. -pub fn find_enum_string_index(cx: *mut JSContext, - v: HandleValue, - values: &[&'static str]) - -> Result<Option<usize>, ()> { - let jsstr = unsafe { ToString(cx, v) }; +pub unsafe fn find_enum_string_index(cx: *mut JSContext, + v: HandleValue, + values: &[&'static str]) + -> Result<Option<usize>, ()> { + let jsstr = ToString(cx, v); if jsstr.is_null() { return Err(()); } diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 9ada3036f1d..b0663192d99 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -157,6 +157,7 @@ impl HTMLCanvasElement { } } + #[allow(unsafe_code)] pub fn get_or_init_webgl_context(&self, cx: *mut JSContext, attrs: Option<HandleValue>) -> Option<Root<WebGLRenderingContext>> { @@ -165,7 +166,7 @@ impl HTMLCanvasElement { let size = self.get_size(); let attrs = if let Some(webgl_attributes) = attrs { - if let Ok(ref attrs) = WebGLContextAttributes::new(cx, webgl_attributes) { + if let Ok(ref attrs) = unsafe { WebGLContextAttributes::new(cx, webgl_attributes) } { From::from(attrs) } else { debug!("Unexpected error on conversion of WebGLContextAttributes"); diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 6bc48da1c4f..1665c60d58f 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -130,6 +130,7 @@ impl HTMLIFrameElement { self.navigate_child_browsing_context(url); } + #[allow(unsafe_code)] pub fn dispatch_mozbrowser_event(&self, event: MozBrowserEvent) { // TODO(gw): Support mozbrowser event types that have detail which is not a string. // See https://developer.mozilla.org/en-US/docs/Web/API/Using_the_Browser_API @@ -138,16 +139,18 @@ impl HTMLIFrameElement { if self.Mozbrowser() { let window = window_from_node(self); - let cx = window.get_cx(); - let _ar = JSAutoRequest::new(cx); - let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get()); - let mut detail = RootedValue::new(cx, UndefinedValue()); - event.detail().to_jsval(cx, detail.handle_mut()); - let custom_event = CustomEvent::new(GlobalRef::Window(window.r()), - DOMString(event.name().to_owned()), - true, - true, - detail.handle()); + let custom_event = unsafe { + let cx = window.get_cx(); + let _ar = JSAutoRequest::new(cx); + let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get()); + let mut detail = RootedValue::new(cx, UndefinedValue()); + event.detail().to_jsval(cx, detail.handle_mut()); + CustomEvent::new(GlobalRef::Window(window.r()), + DOMString(event.name().to_owned()), + true, + true, + detail.handle()) + }; custom_event.upcast::<Event>().fire(self.upcast()); } } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index ca5b14b192c..f96da2f5934 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1935,10 +1935,6 @@ impl NodeMethods for Node { NodeTypeId::CharacterData(..) => { let characterdata = self.downcast::<CharacterData>().unwrap(); characterdata.SetData(value); - - // Notify the document that the content of this node is different - let document = self.owner_doc(); - document.content_changed(self, NodeDamage::OtherNodeDamage); } NodeTypeId::DocumentType | NodeTypeId::Document => {} diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 875f88628f4..c0a00adf102 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -189,19 +189,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { receiver.recv().unwrap() } + #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 fn GetParameter(&self, cx: *mut JSContext, parameter: u32) -> JSVal { // TODO(ecoal95): Implement the missing parameters from the spec - let mut rval = RootedValue::new(cx, UndefinedValue()); - match parameter { - constants::VERSION => - "WebGL 1.0".to_jsval(cx, rval.handle_mut()), - constants::RENDERER | - constants::VENDOR => - "Mozilla/Servo".to_jsval(cx, rval.handle_mut()), - _ => rval.ptr = NullValue(), + unsafe { + let mut rval = RootedValue::new(cx, UndefinedValue()); + match parameter { + constants::VERSION => + "WebGL 1.0".to_jsval(cx, rval.handle_mut()), + constants::RENDERER | + constants::VENDOR => + "Mozilla/Servo".to_jsval(cx, rval.handle_mut()), + _ => rval.ptr = NullValue(), + } + rval.ptr } - rval.ptr } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 @@ -245,6 +248,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14 + fn GetSupportedExtensions(&self) -> Option<Vec<DOMString>> { + Some(vec![]) + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14 fn GetExtension(&self, _cx: *mut JSContext, _name: DOMString) -> *mut JSObject { // TODO(ecoal95) we actually do not support extensions. // `getSupportedExtensions` cannot be implemented as of right now (see #544) diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl index 957220efa54..27019ed7754 100644 --- a/components/script/dom/webidls/WebGLRenderingContext.webidl +++ b/components/script/dom/webidls/WebGLRenderingContext.webidl @@ -471,7 +471,7 @@ interface WebGLRenderingContextBase [WebGLHandlesContextLoss] WebGLContextAttributes? getContextAttributes(); //[WebGLHandlesContextLoss] boolean isContextLost(); - //sequence<DOMString>? getSupportedExtensions(); + sequence<DOMString>? getSupportedExtensions(); object? getExtension(DOMString name); void activeTexture(GLenum texture); diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index ea9c0e243aa..2f7994c373e 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -570,32 +570,32 @@ impl Runnable for MessageReceivedTask { // Step 2-5. let global = ws.global.root(); - let cx = global.r().get_cx(); - let _ar = JSAutoRequest::new(cx); - let _ac = JSAutoCompartment::new(cx, ws.reflector().get_jsobject().get()); - let mut message = RootedValue::new(cx, UndefinedValue()); - match self.message { - MessageData::Text(text) => text.to_jsval(cx, message.handle_mut()), - MessageData::Binary(data) => { - match ws.binary_type.get() { - BinaryType::Blob => { - let blob = Blob::new(global.r(), Some(data), ""); - blob.to_jsval(cx, message.handle_mut()); - } - BinaryType::Arraybuffer => { - unsafe { + // global.get_cx() returns a valid `JSContext` pointer, so this is safe. + unsafe { + let cx = global.r().get_cx(); + let _ar = JSAutoRequest::new(cx); + let _ac = JSAutoCompartment::new(cx, ws.reflector().get_jsobject().get()); + let mut message = RootedValue::new(cx, UndefinedValue()); + match self.message { + MessageData::Text(text) => text.to_jsval(cx, message.handle_mut()), + MessageData::Binary(data) => { + match ws.binary_type.get() { + BinaryType::Blob => { + let blob = Blob::new(global.r(), Some(data), ""); + blob.to_jsval(cx, message.handle_mut()); + } + BinaryType::Arraybuffer => { let len = data.len() as uint32_t; let buf = JS_NewArrayBuffer(cx, len); let buf_data: *mut uint8_t = JS_GetArrayBufferData(buf, ptr::null()); ptr::copy_nonoverlapping(data.as_ptr(), buf_data, len as usize); buf.to_jsval(cx, message.handle_mut()); } - } - } - }, + } + }, + } + MessageEvent::dispatch_jsval(ws.upcast(), global.r(), message.handle()); } - - MessageEvent::dispatch_jsval(ws.upcast(), global.r(), message.handle()); } } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index c7f0af1b234..09f14f12510 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -574,8 +574,9 @@ impl WindowMethods for Window { breakpoint(); } + #[allow(unsafe_code)] fn WebdriverCallback(&self, cx: *mut JSContext, val: HandleValue) { - let rv = jsval_to_webdriver(cx, val); + let rv = unsafe { jsval_to_webdriver(cx, val) }; let opt_chan = self.webdriver_script_chan.borrow_mut().take(); if let Some(chan) = opt_chan { chan.send(rv).unwrap(); diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 770bcaedf78..bdef4b5ebb1 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -45,8 +45,7 @@ use net_traits::ControlMsg::Load; use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata}; use net_traits::{LoadConsumer, LoadData, ResourceCORSData, ResourceTask}; use network_listener::{NetworkListener, PreInvoke}; -use script_task::ScriptTaskEventCategory::XhrEvent; -use script_task::{CommonScriptMsg, Runnable, ScriptChan, ScriptPort}; +use script_task::{ScriptChan, ScriptPort}; use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::cell::{Cell, RefCell}; @@ -139,8 +138,6 @@ pub struct XMLHttpRequest { global: GlobalField, timeout_cancel: DOMRefCell<Option<TimerHandle>>, fetch_time: Cell<i64>, - #[ignore_heap_size_of = "Cannot calculate Heap size"] - timeout_target: DOMRefCell<Option<Box<ScriptChan + Send>>>, generation_id: Cell<GenerationId>, response_status: Cell<Result<(), ()>>, } @@ -175,7 +172,6 @@ impl XMLHttpRequest { global: GlobalField::from_rooted(&global), timeout_cancel: DOMRefCell::new(None), fetch_time: Cell::new(0), - timeout_target: DOMRefCell::new(None), generation_id: Cell::new(GenerationId(0)), response_status: Cell::new(Ok(())), } @@ -279,7 +275,7 @@ impl XMLHttpRequest { ROUTER.add_route(action_receiver.to_opaque(), box move |message| { listener.notify(message.to().unwrap()); }); - resource_task.send(Load(load_data, LoadConsumer::Listener(response_target))).unwrap(); + resource_task.send(Load(load_data, LoadConsumer::Listener(response_target), None)).unwrap(); } } @@ -688,23 +684,23 @@ impl XMLHttpRequestMethods for XMLHttpRequest { #[allow(unsafe_code)] // https://xhr.spec.whatwg.org/#the-response-attribute fn Response(&self, cx: *mut JSContext) -> JSVal { - let mut rval = RootedValue::new(cx, UndefinedValue()); - match self.response_type.get() { - _empty | Text => { - let ready_state = self.ready_state.get(); - if ready_state == XMLHttpRequestState::Done || ready_state == XMLHttpRequestState::Loading { - self.text_response().to_jsval(cx, rval.handle_mut()); - } else { - "".to_jsval(cx, rval.handle_mut()); - } - }, - _ if self.ready_state.get() != XMLHttpRequestState::Done => { - return NullValue() - }, - Json => { - let decoded = UTF_8.decode(&self.response.borrow(), DecoderTrap::Replace).unwrap().to_owned(); - let decoded: Vec<u16> = decoded.utf16_units().collect(); - unsafe { + unsafe { + let mut rval = RootedValue::new(cx, UndefinedValue()); + match self.response_type.get() { + _empty | Text => { + let ready_state = self.ready_state.get(); + if ready_state == XMLHttpRequestState::Done || ready_state == XMLHttpRequestState::Loading { + self.text_response().to_jsval(cx, rval.handle_mut()); + } else { + "".to_jsval(cx, rval.handle_mut()); + } + }, + _ if self.ready_state.get() != XMLHttpRequestState::Done => { + return NullValue() + }, + Json => { + let decoded = UTF_8.decode(&self.response.borrow(), DecoderTrap::Replace).unwrap().to_owned(); + let decoded: Vec<u16> = decoded.utf16_units().collect(); if !JS_ParseJSON(cx, decoded.as_ptr(), decoded.len() as u32, @@ -713,13 +709,13 @@ impl XMLHttpRequestMethods for XMLHttpRequest { return NullValue(); } } + _ => { + // XXXManishearth handle other response types + self.response.borrow().to_jsval(cx, rval.handle_mut()); + } } - _ => { - // XXXManishearth handle other response types - self.response.borrow().to_jsval(cx, rval.handle_mut()); - } + rval.ptr } - rval.ptr } // https://xhr.spec.whatwg.org/#the-responsetext-attribute @@ -919,7 +915,6 @@ impl XMLHttpRequest { fn terminate_ongoing_fetch(&self) { let GenerationId(prev_id) = self.generation_id.get(); self.generation_id.set(GenerationId(prev_id + 1)); - *self.timeout_target.borrow_mut() = None; self.response_status.set(Ok(())); } @@ -958,25 +953,8 @@ impl XMLHttpRequest { self.dispatch_progress_event(false, type_, len, total); } fn set_timeout(&self, duration_ms: u32) { - struct XHRTimeout { - xhr: TrustedXHRAddress, - gen_id: GenerationId, - } - - impl Runnable for XHRTimeout { - fn handler(self: Box<XHRTimeout>) { - let this = *self; - let xhr = this.xhr.root(); - if xhr.ready_state.get() != XMLHttpRequestState::Done { - xhr.process_partial_response(XHRProgress::Errored(this.gen_id, Error::Timeout)); - } - } - } - #[derive(JSTraceable, HeapSizeOf)] struct ScheduledXHRTimeout { - #[ignore_heap_size_of = "Cannot calculate Heap size"] - target: Box<ScriptChan + Send>, #[ignore_heap_size_of = "Because it is non-owning"] xhr: Trusted<XMLHttpRequest>, generation_id: GenerationId, @@ -984,16 +962,15 @@ impl XMLHttpRequest { impl ScheduledCallback for ScheduledXHRTimeout { fn invoke(self: Box<Self>) { - let s = *self; - s.target.send(CommonScriptMsg::RunnableMsg(XhrEvent, box XHRTimeout { - xhr: s.xhr, - gen_id: s.generation_id, - })).unwrap(); + let this = *self; + let xhr = this.xhr.root(); + if xhr.ready_state.get() != XMLHttpRequestState::Done { + xhr.process_partial_response(XHRProgress::Errored(this.generation_id, Error::Timeout)); + } } fn box_clone(&self) -> Box<ScheduledCallback> { box ScheduledXHRTimeout { - target: self.target.clone(), xhr: self.xhr.clone(), generation_id: self.generation_id, } @@ -1004,7 +981,6 @@ impl XMLHttpRequest { // This will cancel all previous timeouts let global = self.global.root(); let callback = ScheduledXHRTimeout { - target: (*self.timeout_target.borrow().as_ref().unwrap()).clone(), xhr: Trusted::new(global.r().get_cx(), self, global.r().script_chan()), generation_id: self.generation_id.get(), }; @@ -1104,7 +1080,6 @@ impl XMLHttpRequest { } else { (global.script_chan(), None) }; - *self.timeout_target.borrow_mut() = Some(script_chan.clone()); let resource_task = global.resource_task(); if let Some(req) = cors_request { diff --git a/components/script/lib.rs b/components/script/lib.rs index e4481fb7d49..3ef9edff9ff 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -12,7 +12,6 @@ #![feature(core_intrinsics)] #![feature(custom_attribute)] #![feature(custom_derive)] -#![feature(decode_utf16)] #![feature(drain)] #![feature(fnbox)] #![feature(hashmap_hasher)] diff --git a/components/script/page.rs b/components/script/page.rs index 67cb1ca46ed..bcfed37b764 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -43,12 +43,14 @@ impl IterablePage for Rc<Page> { } } fn find(&self, id: PipelineId) -> Option<Rc<Page>> { - if self.id == id { return Some(self.clone()); } - for page in &*self.children.borrow() { - let found = page.find(id); - if found.is_some() { return found; } + if self.id == id { + return Some(self.clone()); } - None + + self.children.borrow() + .iter() + .filter_map(|p| p.find(id)) + .next() } } @@ -79,8 +81,8 @@ impl Page { pub fn remove(&self, id: PipelineId) -> Option<Rc<Page>> { let remove_idx = { self.children - .borrow_mut() - .iter_mut() + .borrow() + .iter() .position(|page_tree| page_tree.id == id) }; match remove_idx { @@ -100,15 +102,11 @@ impl Iterator for PageIterator { type Item = Rc<Page>; fn next(&mut self) -> Option<Rc<Page>> { - match self.stack.pop() { - Some(next) => { - for child in &*next.children.borrow() { - self.stack.push(child.clone()); - } - Some(next) - }, - None => None, + let popped = self.stack.pop(); + if let Some(ref page) = popped { + self.stack.extend(page.children.borrow().iter().cloned()); } + popped } } diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 28e194b2a4a..48972b3fe70 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -236,7 +236,6 @@ pub enum ScriptTaskEventCategory { SetViewport, WebSocketEvent, WorkerEvent, - XhrEvent, } /// Messages used to control the script event loop @@ -954,7 +953,6 @@ impl ScriptTask { ScriptTaskEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent, ScriptTaskEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent, ScriptTaskEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent, - ScriptTaskEventCategory::XhrEvent => ProfilerCategory::ScriptXhrEvent, }; profile(profiler_cat, None, self.time_profiler_chan.clone(), f) } else { @@ -1685,11 +1683,13 @@ impl ScriptTask { let script_source = String::from_utf8_lossy(&script_source_bytes); // Script source is ready to be evaluated (11.) - let mut jsval = RootedValue::new(self.get_cx(), UndefinedValue()); - window.evaluate_js_on_global_with_result(&script_source, jsval.handle_mut()); - let strval = DOMString::from_jsval(self.get_cx(), jsval.handle(), - StringificationBehavior::Empty); - strval.unwrap_or(DOMString::new()) + unsafe { + let mut jsval = RootedValue::new(self.get_cx(), UndefinedValue()); + window.evaluate_js_on_global_with_result(&script_source, jsval.handle_mut()); + let strval = DOMString::from_jsval(self.get_cx(), jsval.handle(), + StringificationBehavior::Empty); + strval.unwrap_or(DOMString::new()) + } } else { DOMString::new() }; @@ -1965,7 +1965,7 @@ impl ScriptTask { data: load_data.data, cors: None, pipeline_id: Some(id), - }, LoadConsumer::Listener(response_target))).unwrap(); + }, LoadConsumer::Listener(response_target), None)).unwrap(); self.incomplete_loads.borrow_mut().push(incomplete); } diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 3fcd6ef1d0c..9f095fab309 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -40,7 +40,8 @@ fn find_node_by_unique_id(page: &Rc<Page>, pipeline: PipelineId, node_id: String None } -pub fn jsval_to_webdriver(cx: *mut JSContext, val: HandleValue) -> WebDriverJSResult { +#[allow(unsafe_code)] +pub unsafe fn jsval_to_webdriver(cx: *mut JSContext, val: HandleValue) -> WebDriverJSResult { if val.get().is_undefined() { Ok(WebDriverJSValue::Undefined) } else if val.get().is_boolean() { @@ -58,17 +59,20 @@ pub fn jsval_to_webdriver(cx: *mut JSContext, val: HandleValue) -> WebDriverJSRe } } +#[allow(unsafe_code)] pub fn handle_execute_script(page: &Rc<Page>, pipeline: PipelineId, eval: String, reply: IpcSender<WebDriverJSResult>) { let page = get_page(&*page, pipeline); let window = page.window(); - let cx = window.get_cx(); - let mut rval = RootedValue::new(cx, UndefinedValue()); - window.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); - - reply.send(jsval_to_webdriver(cx, rval.handle())).unwrap(); + let result = unsafe { + let cx = window.get_cx(); + let mut rval = RootedValue::new(cx, UndefinedValue()); + window.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); + jsval_to_webdriver(cx, rval.handle()) + }; + reply.send(result).unwrap(); } pub fn handle_execute_async_script(page: &Rc<Page>, diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 9d94e5c6038..2e18cd64b3c 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -936,12 +936,13 @@ dependencies = [ [[package]] name = "js" version = "0.1.1" -source = "git+https://github.com/servo/rust-mozjs#e2458c40432119693456c73ac0710ff182cfc6d2" +source = "git+https://github.com/servo/rust-mozjs#16a084022c58337f0e6f591a084a1dbd9e8fe814" dependencies = [ "heapsize 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "mozjs_sys 0.0.0 (git+https://github.com/servo/mozjs)", + "num 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/components/util/lib.rs b/components/util/lib.rs index 850998408f1..abcb686723a 100644 --- a/components/util/lib.rs +++ b/components/util/lib.rs @@ -6,6 +6,7 @@ #![feature(box_syntax)] #![feature(core_intrinsics)] #![feature(custom_derive)] +#![feature(decode_utf16)] #![feature(fnbox)] #![feature(hashmap_hasher)] #![feature(heap_api)] diff --git a/components/util/str.rs b/components/util/str.rs index 80e5fb04d6c..afbced05ac4 100644 --- a/components/util/str.rs +++ b/components/util/str.rs @@ -4,15 +4,23 @@ use app_units::Au; use cssparser::{self, Color, RGBA}; +use js::conversions::{FromJSValConvertible, ToJSValConvertible, latin1_to_string}; +use js::jsapi::{JSContext, JSString, HandleValue, MutableHandleValue}; +use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_StringHasLatin1Chars}; +use js::rust::ToString; use libc::c_char; use num_lib::ToPrimitive; +use opts; use std::ascii::AsciiExt; use std::borrow::ToOwned; +use std::char; use std::convert::AsRef; use std::ffi::CStr; use std::fmt; use std::iter::{Filter, Peekable}; use std::ops::{Deref, DerefMut}; +use std::ptr; +use std::slice; use std::str::{CharIndices, FromStr, Split, from_utf8}; #[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Deserialize, Serialize, Hash, Debug)] @@ -71,6 +79,82 @@ impl Into<Vec<u8>> for DOMString { } } +// https://heycam.github.io/webidl/#es-DOMString +impl ToJSValConvertible for DOMString { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + (**self).to_jsval(cx, rval); + } +} + +/// Behavior for stringification of `JSVal`s. +#[derive(PartialEq)] +pub enum StringificationBehavior { + /// Convert `null` to the string `"null"`. + Default, + /// Convert `null` to the empty string. + Empty, +} + +/// Convert the given `JSString` to a `DOMString`. Fails if the string does not +/// contain valid UTF-16. +pub unsafe fn jsstring_to_str(cx: *mut JSContext, s: *mut JSString) -> DOMString { + let latin1 = JS_StringHasLatin1Chars(s); + DOMString(if latin1 { + latin1_to_string(cx, s) + } else { + let mut length = 0; + let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), s, &mut length); + assert!(!chars.is_null()); + let potentially_ill_formed_utf16 = slice::from_raw_parts(chars, length as usize); + let mut s = String::with_capacity(length as usize); + for item in char::decode_utf16(potentially_ill_formed_utf16.iter().cloned()) { + match item { + Ok(c) => s.push(c), + Err(_) => { + // FIXME: Add more info like document URL in the message? + macro_rules! message { + () => { + "Found an unpaired surrogate in a DOM string. \ + If you see this in real web content, \ + please comment on https://github.com/servo/servo/issues/6564" + } + } + if opts::get().replace_surrogates { + error!(message!()); + s.push('\u{FFFD}'); + } else { + panic!(concat!(message!(), " Use `-Z replace-surrogates` \ + on the command line to make this non-fatal.")); + } + } + } + } + s + }) +} + +// https://heycam.github.io/webidl/#es-DOMString +impl FromJSValConvertible for DOMString { + type Config = StringificationBehavior; + unsafe fn from_jsval(cx: *mut JSContext, + value: HandleValue, + null_behavior: StringificationBehavior) + -> Result<DOMString, ()> { + if null_behavior == StringificationBehavior::Empty && + value.get().is_null() { + Ok(DOMString::new()) + } else { + let jsstr = ToString(cx, value); + if jsstr.is_null() { + debug!("ToString failed"); + Err(()) + } else { + Ok(jsstring_to_str(cx, jsstr)) + } + } + } +} + pub type StaticCharVec = &'static [char]; pub type StaticStringVec = &'static [&'static str]; |