diff options
Diffstat (limited to 'components/script')
26 files changed, 669 insertions, 887 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 056bb6f72bb..95f1044fd36 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -60,7 +60,6 @@ smallvec = "0.1" string_cache = {version = "0.2.18", features = ["heap_size", "unstable"]} style = {path = "../style"} time = "0.1.12" -unicase = "1.0" url = {version = "1.0.0", features = ["heap_size", "query_encoding"]} util = {path = "../util"} uuid = {version = "0.2", features = ["v4"]} diff --git a/components/script/cors.rs b/components/script/cors.rs deleted file mode 100644 index f9d0bbbd264..00000000000 --- a/components/script/cors.rs +++ /dev/null @@ -1,490 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//! A partial implementation of CORS -//! For now this library is XHR-specific. -//! For stuff involving `<img>`, `<iframe>`, `<form>`, etc please check what -//! the request mode should be and compare with the fetch spec -//! This library will eventually become the core of the Fetch crate -//! with CORSRequest being expanded into FetchRequest (etc) - -use hyper::client::Request; -use hyper::header::{AccessControlAllowHeaders, AccessControlRequestHeaders}; -use hyper::header::{AccessControlAllowMethods, AccessControlRequestMethod}; -use hyper::header::{AccessControlAllowOrigin, AccessControlMaxAge}; -use hyper::header::{ContentType, Host}; -use hyper::header::{HeaderView, Headers}; -use hyper::method::Method; -use hyper::mime::{Mime, SubLevel, TopLevel}; -use hyper::status::StatusClass::Success; -use net_traits::{AsyncResponseListener, Metadata, NetworkError, ResponseAction}; -use network_listener::{NetworkListener, PreInvoke}; -use script_runtime::ScriptChan; -use std::ascii::AsciiExt; -use std::borrow::ToOwned; -use std::sync::{Arc, Mutex}; -use time::{self, Timespec, now}; -use unicase::UniCase; -use url::Url; -use util::thread::spawn_named; - -/// Interface for network listeners concerned with CORS checks. Proper network requests -/// should be initiated from this method, based on the response provided. -pub trait AsyncCORSResponseListener { - fn response_available(&self, response: CORSResponse); -} - -#[derive(Clone, HeapSizeOf)] -pub struct CORSRequest { - pub origin: Url, - pub destination: Url, - pub mode: RequestMode, - #[ignore_heap_size_of = "Defined in hyper"] - pub method: Method, - #[ignore_heap_size_of = "Defined in hyper"] - pub headers: Headers, - /// CORS preflight flag (https://fetch.spec.whatwg.org/#concept-http-fetch) - /// Indicates that a CORS preflight request and/or cache check is to be performed - pub preflight_flag: bool, -} - -/// https://fetch.spec.whatwg.org/#concept-request-mode -/// This only covers some of the request modes. The -/// `same-origin` and `no CORS` modes are unnecessary for XHR. -#[derive(PartialEq, Copy, Clone, HeapSizeOf)] -pub enum RequestMode { - CORS, // CORS - ForcedPreflight, // CORS-with-forced-preflight -} - -impl CORSRequest { - /// Creates a CORS request if necessary. Will return an error when fetching is forbidden - pub fn maybe_new(referer: Url, - destination: Url, - mode: RequestMode, - method: Method, - headers: Headers, - same_origin_data_url_flag: bool) - -> Result<Option<CORSRequest>, ()> { - if referer.origin() == destination.origin() { - return Ok(None); // Not cross-origin, proceed with a normal fetch - } - match destination.scheme() { - // As per (https://fetch.spec.whatwg.org/#main-fetch 5.1.9), about URLs can be fetched - // the same as a basic request. - "about" if destination.path() == "blank" => Ok(None), - // As per (https://fetch.spec.whatwg.org/#main-fetch 5.1.9), data URLs can be fetched - // the same as a basic request if the request's method is GET and the - // same-origin data-URL flag is set. - "data" if same_origin_data_url_flag && method == Method::Get => Ok(None), - "http" | "https" => { - let mut req = CORSRequest::new(referer, destination, mode, method, headers); - req.preflight_flag = !is_simple_method(&req.method) || - mode == RequestMode::ForcedPreflight; - if req.headers.iter().any(|h| !is_simple_header(&h)) { - req.preflight_flag = true; - } - Ok(Some(req)) - }, - _ => Err(()), - } - } - - fn new(mut referer: Url, - destination: Url, - mode: RequestMode, - method: Method, - headers: Headers) - -> CORSRequest { - referer.set_fragment(None); - referer.set_query(None); - referer.set_path(""); - CORSRequest { - origin: referer, - destination: destination, - mode: mode, - method: method, - headers: headers, - preflight_flag: false, - } - } - - pub fn http_fetch_async(&self, - listener: Box<AsyncCORSResponseListener + Send>, - script_chan: Box<ScriptChan + Send>) { - struct CORSContext { - listener: Box<AsyncCORSResponseListener + Send>, - response: Option<CORSResponse>, - } - - // This is shoe-horning the CORSReponse stuff into the rest of the async network - // framework right now. It would be worth redesigning http_fetch to do this properly. - impl AsyncResponseListener for CORSContext { - fn headers_available(&mut self, _metadata: Result<Metadata, NetworkError>) { - } - - fn data_available(&mut self, _payload: Vec<u8>) { - } - - fn response_complete(&mut self, _status: Result<(), NetworkError>) { - let response = self.response.take().unwrap(); - self.listener.response_available(response); - } - } - impl PreInvoke for CORSContext {} - - let context = CORSContext { - listener: listener, - response: None, - }; - let listener = NetworkListener { - context: Arc::new(Mutex::new(context)), - script_chan: script_chan, - }; - - // TODO: this exists only to make preflight check non-blocking - // perhaps should be handled by the resource thread? - let req = self.clone(); - spawn_named("cors".to_owned(), move || { - let response = req.http_fetch(); - let mut context = listener.context.lock(); - let context = context.as_mut().unwrap(); - context.response = Some(response); - listener.notify(ResponseAction::ResponseComplete(Ok(()))); - }); - } - - /// http://fetch.spec.whatwg.org/#concept-http-fetch - /// This method assumes that the CORS flag is set - /// This does not perform the full HTTP fetch, rather it handles part of the CORS filtering - /// if self.mode is ForcedPreflight, then the CORS-with-forced-preflight - /// fetch flag is set as well - pub fn http_fetch(&self) -> CORSResponse { - let response = CORSResponse::new(); - // Step 2: Handle service workers (unimplemented) - // Step 3 - // Substep 1: Service workers (unimplemented ) - // Substep 2 - let cache = &mut CORSCache(vec!()); // XXXManishearth Should come from user agent - if self.preflight_flag && - !cache.match_method(self, &self.method) && - !self.headers.iter().all(|h| is_simple_header(&h) && cache.match_header(self, h.name())) && - (!is_simple_method(&self.method) || self.mode == RequestMode::ForcedPreflight) { - return self.preflight_fetch(); - // Everything after this is part of XHR::fetch() - // Expect the organization of code to improve once we have a fetch crate - } - response - } - - /// https://fetch.spec.whatwg.org/#cors-preflight-fetch - fn preflight_fetch(&self) -> CORSResponse { - let error = CORSResponse::new_error(); - let mut cors_response = CORSResponse::new(); - - // Step 1 - let mut preflight = self.clone(); - preflight.method = Method::Options; - preflight.headers = Headers::new(); - // Step 2 - preflight.headers.set(AccessControlRequestMethod(self.method.clone())); - - // Steps 3-5 - let mut header_names = vec![]; - for header in self.headers.iter() { - header_names.push(header.name().to_owned()); - } - header_names.sort(); - preflight.headers - .set(AccessControlRequestHeaders(header_names.into_iter().map(UniCase).collect())); - - let preflight_request = Request::new(preflight.method, preflight.destination); - let mut req = match preflight_request { - Ok(req) => req, - Err(_) => return error, - }; - - let host = req.headers().get::<Host>().unwrap().clone(); - *req.headers_mut() = preflight.headers.clone(); - req.headers_mut().set(host); - let stream = match req.start() { - Ok(s) => s, - Err(_) => return error, - }; - // Step 6 - let response = match stream.send() { - Ok(r) => r, - Err(_) => return error, - }; - - // Step 7: We don't perform a CORS check here - // FYI, fn allow_cross_origin_request() performs the CORS check - match response.status.class() { - Success => {} - _ => return error, - } - cors_response.headers = response.headers.clone(); - // Substeps 1-3 (parsing rules: https://fetch.spec.whatwg.org/#http-new-header-syntax) - let methods_substep4 = [self.method.clone()]; - let mut methods = match response.headers.get() { - Some(&AccessControlAllowMethods(ref v)) => &**v, - _ => return error, - }; - let headers = match response.headers.get() { - Some(&AccessControlAllowHeaders(ref h)) => h, - _ => return error, - }; - // Substep 4 - if methods.is_empty() && preflight.mode == RequestMode::ForcedPreflight { - methods = &methods_substep4; - } - // Substep 5 - if !is_simple_method(&self.method) && !methods.iter().any(|m| m == &self.method) { - return error; - } - // Substep 6 - for h in self.headers.iter() { - if is_simple_header(&h) { - continue; - } - if !headers.iter().any(|ref h2| h.name().eq_ignore_ascii_case(h2)) { - return error; - } - } - // Substeps 7-8 - let max_age = match response.headers.get() { - Some(&AccessControlMaxAge(num)) => num, - None => 0, - }; - // Substep 9: Impose restrictions on max-age, if any (unimplemented) - // Substeps 10-12: Add a cache (partially implemented, XXXManishearth) - // This cache should come from the user agent, creating a new one here to check - // for compile time errors - let cache = &mut CORSCache(vec![]); - for m in methods { - let cache_match = cache.match_method_and_update(self, m, max_age); - if !cache_match { - cache.insert(CORSCacheEntry::new(self.origin.clone(), - self.destination.clone(), - max_age, - false, - HeaderOrMethod::MethodData(m.clone()))); - } - } - // Substeps 13-14 - for h in response.headers.iter() { - let cache_match = cache.match_header_and_update(self, h.name(), max_age); - if !cache_match { - cache.insert(CORSCacheEntry::new(self.origin.clone(), - self.destination.clone(), - max_age, - false, - HeaderOrMethod::HeaderData(h.to_string()))); - } - } - // Substep 15 - cors_response - } -} - - -pub struct CORSResponse { - pub network_error: bool, - pub headers: Headers, -} - -impl CORSResponse { - fn new() -> CORSResponse { - CORSResponse { - network_error: false, - headers: Headers::new(), - } - } - - fn new_error() -> CORSResponse { - CORSResponse { - network_error: true, - headers: Headers::new(), - } - } -} - -// CORS Cache stuff - -/// A CORS cache object. Anchor it somewhere to the user agent. -#[derive(Clone)] -pub struct CORSCache(Vec<CORSCacheEntry>); - -/// Union type for CORS cache entries -/// Each entry might pertain to a header or method -#[derive(Clone)] -pub enum HeaderOrMethod { - HeaderData(String), - MethodData(Method), -} - -impl HeaderOrMethod { - fn match_header(&self, header_name: &str) -> bool { - match *self { - HeaderOrMethod::HeaderData(ref s) => (&**s).eq_ignore_ascii_case(header_name), - _ => false, - } - } - - fn match_method(&self, method: &Method) -> bool { - match *self { - HeaderOrMethod::MethodData(ref m) => m == method, - _ => false, - } - } -} - -// An entry in the CORS cache -#[derive(Clone)] -pub struct CORSCacheEntry { - pub origin: Url, - pub url: Url, - pub max_age: u32, - pub credentials: bool, - pub header_or_method: HeaderOrMethod, - created: Timespec, -} - -impl CORSCacheEntry { - fn new(origin: Url, - url: Url, - max_age: u32, - credentials: bool, - header_or_method: HeaderOrMethod) - -> CORSCacheEntry { - CORSCacheEntry { - origin: origin, - url: url, - max_age: max_age, - credentials: credentials, - header_or_method: header_or_method, - created: time::now().to_timespec(), - } - } -} - -impl CORSCache { - /// https://fetch.spec.whatwg.org/#concept-cache-clear - #[allow(dead_code)] - fn clear(&mut self, request: &CORSRequest) { - let CORSCache(buf) = self.clone(); - let new_buf: Vec<CORSCacheEntry> = - buf.into_iter() - .filter(|e| e.origin == request.origin && request.destination == e.url) - .collect(); - *self = CORSCache(new_buf); - } - - // Remove old entries - fn cleanup(&mut self) { - let CORSCache(buf) = self.clone(); - let now = time::now().to_timespec(); - let new_buf: Vec<CORSCacheEntry> = buf.into_iter() - .filter(|e| now.sec > e.created.sec + e.max_age as i64) - .collect(); - *self = CORSCache(new_buf); - } - - /// https://fetch.spec.whatwg.org/#concept-cache-match-header - fn find_entry_by_header<'a>(&'a mut self, - request: &CORSRequest, - header_name: &str) - -> Option<&'a mut CORSCacheEntry> { - self.cleanup(); - // Credentials are not yet implemented here - self.0.iter_mut().find(|e| { - e.origin.scheme() == request.origin.scheme() && - e.origin.host_str() == request.origin.host_str() && - e.origin.port() == request.origin.port() && - e.url == request.destination && - e.header_or_method.match_header(header_name) - }) - } - - fn match_header(&mut self, request: &CORSRequest, header_name: &str) -> bool { - self.find_entry_by_header(request, header_name).is_some() - } - - fn match_header_and_update(&mut self, - request: &CORSRequest, - header_name: &str, - new_max_age: u32) - -> bool { - self.find_entry_by_header(request, header_name).map(|e| e.max_age = new_max_age).is_some() - } - - fn find_entry_by_method<'a>(&'a mut self, - request: &CORSRequest, - method: &Method) - -> Option<&'a mut CORSCacheEntry> { - // we can take the method from CORSRequest itself - self.cleanup(); - // Credentials are not yet implemented here - self.0.iter_mut().find(|e| { - e.origin.scheme() == request.origin.scheme() && - e.origin.host_str() == request.origin.host_str() && - e.origin.port() == request.origin.port() && - e.url == request.destination && - e.header_or_method.match_method(method) - }) - } - - /// https://fetch.spec.whatwg.org/#concept-cache-match-method - fn match_method(&mut self, request: &CORSRequest, method: &Method) -> bool { - self.find_entry_by_method(request, method).is_some() - } - - fn match_method_and_update(&mut self, - request: &CORSRequest, - method: &Method, - new_max_age: u32) - -> bool { - self.find_entry_by_method(request, method).map(|e| e.max_age = new_max_age).is_some() - } - - fn insert(&mut self, entry: CORSCacheEntry) { - self.cleanup(); - self.0.push(entry); - } -} - -fn is_simple_header(h: &HeaderView) -> bool { - // FIXME: use h.is::<HeaderType>() when AcceptLanguage and - // ContentLanguage headers exist - match &*h.name().to_ascii_lowercase() { - "accept" | "accept-language" | "content-language" => true, - "content-type" => match h.value() { - Some(&ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) | - Some(&ContentType(Mime(TopLevel::Application, SubLevel::WwwFormUrlEncoded, _))) | - Some(&ContentType(Mime(TopLevel::Multipart, SubLevel::FormData, _))) => true, - - _ => false, - - }, - _ => false, - } -} - -fn is_simple_method(m: &Method) -> bool { - match *m { - Method::Get | Method::Head | Method::Post => true, - _ => false, - } -} - -/// Perform a CORS check on a header list and CORS request -/// https://fetch.spec.whatwg.org/#cors-check -pub fn allow_cross_origin_request(req: &CORSRequest, headers: &Headers) -> bool { - match headers.get::<AccessControlAllowOrigin>() { - Some(&AccessControlAllowOrigin::Any) => true, // Not always true, depends on credentials mode - Some(&AccessControlAllowOrigin::Value(ref url)) => req.origin.as_str() == *url, - Some(&AccessControlAllowOrigin::Null) | - None => false, - } -} diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf index 314151f8c58..44d835589c4 100644 --- a/components/script/dom/bindings/codegen/Bindings.conf +++ b/components/script/dom/bindings/codegen/Bindings.conf @@ -23,6 +23,12 @@ DOMInterfaces = { 'URL': { 'weakReferenceable': True, +}, + +'WindowProxy' : { + 'nativeType': 'BrowsingContext', + 'path': 'dom::browsingcontext::BrowsingContext', + 'register': False, } } diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index c356a7d066c..057246f05aa 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -1695,7 +1695,7 @@ class CGImports(CGWrapper): """ Generates the appropriate import/use statements. """ - def __init__(self, child, descriptors, callbacks, imports, ignored_warnings=None): + def __init__(self, child, descriptors, callbacks, imports, config, ignored_warnings=None): """ Adds a set of imports. """ @@ -1756,7 +1756,11 @@ class CGImports(CGWrapper): for c in callbacks: types += relatedTypesForSignatures(c) - imports += ['dom::types::%s' % getIdentifier(t).name for t in types if isImportable(t)] + descriptorProvider = config.getDescriptorProvider() + for t in types: + if isImportable(t): + descriptor = descriptorProvider.getDescriptor(getIdentifier(t).name) + imports += ['%s' % descriptor.path] statements = [] if len(ignored_warnings) > 0: @@ -2090,7 +2094,7 @@ def UnionTypes(descriptors, dictionaries, callbacks, config): # Sort unionStructs by key, retrieve value unionStructs = (i[1] for i in sorted(unionStructs.items(), key=operator.itemgetter(0))) - return CGImports(CGList(unionStructs, "\n\n"), [], [], imports, ignored_warnings=[]) + return CGImports(CGList(unionStructs, "\n\n"), [], [], imports, config, ignored_warnings=[]) class Argument(): @@ -5460,7 +5464,8 @@ class CGBindingRoot(CGThing): # (hence hasInterfaceObject=False). descriptors.extend(config.getDescriptors(webIDLFile=webIDLFile, hasInterfaceObject=False, - isCallback=False)) + isCallback=False, + register=True)) dictionaries = config.getDictionaries(webIDLFile=webIDLFile) @@ -5588,6 +5593,7 @@ class CGBindingRoot(CGThing): 'dom::bindings::str::{ByteString, DOMString, USVString}', 'dom::bindings::trace::RootedVec', 'dom::bindings::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable}', + 'dom::browsingcontext::BrowsingContext', 'mem::heap_size_of_raw_self_and_children', 'libc', 'util::prefs', @@ -5602,7 +5608,7 @@ class CGBindingRoot(CGThing): 'std::rc::Rc', 'std::default::Default', 'std::ffi::CString', - ]) + ], config) # Add the auto-generated comment. curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) @@ -6278,7 +6284,7 @@ class GlobalGenRoots(): 'dom::bindings::codegen', 'dom::bindings::codegen::PrototypeList::Proxies', 'libc', - ], ignored_warnings=[]) + ], config, ignored_warnings=[]) @staticmethod def InterfaceTypes(config): diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py index 3ee7fb37e77..7aad0f75f75 100644 --- a/components/script/dom/bindings/codegen/Configuration.py +++ b/components/script/dom/bindings/codegen/Configuration.py @@ -173,6 +173,7 @@ class Descriptor(DescriptorProvider): # Read the desc, and fill in the relevant defaults. ifaceName = self.interface.identifier.name + typeName = desc.get('nativeType', ifaceName) # Callback types do not use JS smart pointers, so we should not use the # built-in rooting mechanisms for them. @@ -184,12 +185,13 @@ class Descriptor(DescriptorProvider): self.nativeType = ty else: self.needsRooting = True - self.returnType = "Root<%s>" % ifaceName - self.argumentType = "&%s" % ifaceName - self.nativeType = "*const %s" % ifaceName + self.returnType = "Root<%s>" % typeName + self.argumentType = "&%s" % typeName + self.nativeType = "*const %s" % typeName - self.concreteType = ifaceName + self.concreteType = typeName self.register = desc.get('register', True) + self.path = desc.get('path', 'dom::types::%s' % typeName) self.outerObjectHook = desc.get('outerObjectHook', 'None') self.proxy = False self.weakReferenceable = desc.get('weakReferenceable', False) diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index f33a8834a08..34ec7d19e14 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -43,6 +43,7 @@ use dom::bindings::utils::WindowProxyHandler; use encoding::types::EncodingRef; use euclid::length::Length as EuclidLength; use euclid::matrix2d::Matrix2D; +use euclid::point::Point2D; use euclid::rect::Rect; use euclid::size::Size2D; use html5ever::tree_builder::QuirksMode; @@ -57,6 +58,7 @@ use js::rust::Runtime; use layout_interface::LayoutRPC; use libc; use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeData, WindowSizeType, ReferrerPolicy}; +use net_traits::filemanager_thread::SelectedFileId; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; use net_traits::response::HttpsState; @@ -278,6 +280,7 @@ no_jsmanaged_fields!(usize, u8, u16, u32, u64); no_jsmanaged_fields!(isize, i8, i16, i32, i64); no_jsmanaged_fields!(Sender<T>); no_jsmanaged_fields!(Receiver<T>); +no_jsmanaged_fields!(Point2D<T>); no_jsmanaged_fields!(Rect<T>); no_jsmanaged_fields!(Size2D<T>); no_jsmanaged_fields!(Arc<T>); @@ -324,6 +327,7 @@ no_jsmanaged_fields!(USVString); no_jsmanaged_fields!(ReferrerPolicy); no_jsmanaged_fields!(ResourceThreads); no_jsmanaged_fields!(SystemTime); +no_jsmanaged_fields!(SelectedFileId); impl JSTraceable for Box<ScriptChan + Send> { #[inline] diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index b89aa3b9ac2..d8beec236a8 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -14,14 +14,13 @@ use dom::bindings::str::DOMString; use encoding::all::UTF_8; use encoding::types::{EncoderTrap, Encoding}; use ipc_channel::ipc; -use net_traits::filemanager_thread::FileManagerThreadMsg; +use net_traits::filemanager_thread::{FileManagerThreadMsg, SelectedFileId}; use num_traits::ToPrimitive; use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::cell::Cell; use std::cmp::{max, min}; use std::sync::Arc; -use uuid::Uuid; #[derive(Clone, JSTraceable)] pub struct DataSlice { @@ -95,7 +94,7 @@ impl DataSlice { #[derive(Clone, JSTraceable)] pub enum BlobImpl { /// File-based, cached backend - File(Uuid, DOMRefCell<Option<DataSlice>>), + File(SelectedFileId, DOMRefCell<Option<DataSlice>>), /// Memory-based backend Memory(DataSlice), } @@ -107,7 +106,7 @@ impl BlobImpl { } /// Construct file-backed BlobImpl from File ID - pub fn new_from_file(file_id: Uuid) -> BlobImpl { + pub fn new_from_file(file_id: SelectedFileId) -> BlobImpl { BlobImpl::File(file_id, DOMRefCell::new(None)) } @@ -184,7 +183,7 @@ impl Blob { } } -fn read_file(global: GlobalRef, id: Uuid) -> Result<DataSlice, ()> { +fn read_file(global: GlobalRef, id: SelectedFileId) -> Result<DataSlice, ()> { let file_manager = global.filemanager_thread(); let (chan, recv) = ipc::channel().map_err(|_|())?; let _ = file_manager.send(FileManagerThreadMsg::ReadFile(chan, id)); diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 1c0e0c9cf1a..57248c03e97 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -19,11 +19,13 @@ use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::error::{Error, ErrorResult}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; -use dom::bindings::js::{Root, LayoutJS}; +use dom::bindings::js::{JS, MutNullableHeap, Root, LayoutJS}; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; +use dom::browsingcontext::BrowsingContext; use dom::customevent::CustomEvent; use dom::document::Document; +use dom::domtokenlist::DOMTokenList; use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers}; use dom::event::Event; use dom::eventtarget::EventTarget; @@ -63,13 +65,14 @@ pub struct HTMLIFrameElement { htmlelement: HTMLElement, pipeline_id: Cell<Option<PipelineId>>, subpage_id: Cell<Option<SubpageId>>, - sandbox: Cell<Option<u8>>, + sandbox: MutNullableHeap<JS<DOMTokenList>>, + sandbox_allowance: Cell<Option<u8>>, load_blocker: DOMRefCell<Option<LoadBlocker>>, } impl HTMLIFrameElement { pub fn is_sandboxed(&self) -> bool { - self.sandbox.get().is_some() + self.sandbox_allowance.get().is_some() } /// <https://html.spec.whatwg.org/multipage/#otherwise-steps-for-iframe-or-frame-elements>, @@ -193,7 +196,8 @@ impl HTMLIFrameElement { htmlelement: HTMLElement::new_inherited(localName, prefix, document), pipeline_id: Cell::new(None), subpage_id: Cell::new(None), - sandbox: Cell::new(None), + sandbox: Default::default(), + sandbox_allowance: Cell::new(None), load_blocker: DOMRefCell::new(None), } } @@ -256,6 +260,15 @@ impl HTMLIFrameElement { } } + pub fn get_content_window(&self) -> Option<Root<Window>> { + self.subpage_id.get().and_then(|subpage_id| { + let window = window_from_node(self); + let window = window.r(); + let browsing_context = window.browsing_context(); + browsing_context.find_child_by_subpage(subpage_id) + }) + } + } pub trait HTMLIFrameElementLayoutMethods { @@ -412,28 +425,21 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement { } // https://html.spec.whatwg.org/multipage/#dom-iframe-sandbox - fn Sandbox(&self) -> DOMString { - self.upcast::<Element>().get_string_attribute(&atom!("sandbox")) - } - - // https://html.spec.whatwg.org/multipage/#dom-iframe-sandbox - fn SetSandbox(&self, sandbox: DOMString) { - self.upcast::<Element>().set_tokenlist_attribute(&atom!("sandbox"), sandbox); + fn Sandbox(&self) -> Root<DOMTokenList> { + self.sandbox.or_init(|| DOMTokenList::new(self.upcast::<Element>(), &atom!("sandbox"))) } // https://html.spec.whatwg.org/multipage/#dom-iframe-contentwindow - fn GetContentWindow(&self) -> Option<Root<Window>> { - self.subpage_id.get().and_then(|subpage_id| { - let window = window_from_node(self); - let window = window.r(); - let browsing_context = window.browsing_context(); - browsing_context.find_child_by_subpage(subpage_id) - }) + fn GetContentWindow(&self) -> Option<Root<BrowsingContext>> { + match self.get_content_window() { + Some(ref window) => Some(window.browsing_context()), + None => None + } } // https://html.spec.whatwg.org/multipage/#dom-iframe-contentdocument fn GetContentDocument(&self) -> Option<Root<Document>> { - self.GetContentWindow().and_then(|window| { + self.get_content_window().and_then(|window| { // FIXME(#10964): this should use the Document's origin and the // origin of the incumbent settings object. let self_url = self.get_url(); @@ -515,7 +521,7 @@ impl VirtualMethods for HTMLIFrameElement { self.super_type().unwrap().attribute_mutated(attr, mutation); match attr.local_name() { &atom!("sandbox") => { - self.sandbox.set(mutation.new_value(attr).map(|value| { + self.sandbox_allowance.set(mutation.new_value(attr).map(|value| { let mut modes = SandboxAllowance::AllowNothing as u8; for token in value.as_tokens() { modes |= match &*token.to_ascii_lowercase() { diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 2b0056a8d65..79b6ed32f0b 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -227,7 +227,7 @@ impl HTMLLinkElement { sender: action_sender, }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { - listener.notify(message.to().unwrap()); + listener.notify_action(message.to().unwrap()); }); if self.parser_inserted.get() { diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 7256ae1b131..70563ec95c7 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -484,7 +484,7 @@ impl HTMLMediaElement { sender: action_sender, }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { - listener.notify(message.to().unwrap()); + listener.notify_action(message.to().unwrap()); }); // FIXME: we're supposed to block the load event much earlier than now diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 1f9f49516dd..f280f670e4b 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -314,7 +314,7 @@ impl HTMLScriptElement { sender: action_sender, }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { - listener.notify(message.to().unwrap()); + listener.notify_action(message.to().unwrap()); }); doc.load_async(LoadType::Script(url), response_target); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 3032abe0685..a3289cca56e 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -298,6 +298,10 @@ impl Node { self.owner_doc().content_and_heritage_changed(self, NodeDamage::OtherNodeDamage); child.owner_doc().content_and_heritage_changed(child, NodeDamage::OtherNodeDamage); } + + pub fn to_untrusted_node_address(&self) -> UntrustedNodeAddress { + UntrustedNodeAddress(self.reflector().get_jsobject().get() as *const c_void) + } } pub struct QuerySelectorIterator { @@ -622,7 +626,7 @@ impl Node { pub fn scroll_offset(&self) -> Point2D<f32> { let document = self.owner_doc(); let window = document.window(); - window.scroll_offset_query(self.to_trusted_node_address()) + window.scroll_offset_query(self) } // https://dom.spec.whatwg.org/#dom-childnode-before diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 70379e772d2..b23ec913eca 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -40,6 +40,7 @@ use util::vec::byte_swap; use webrender_traits::WebGLError::*; use webrender_traits::{WebGLCommand, WebGLError, WebGLFramebufferBindingRequest, WebGLParameter}; +type ImagePixelResult = Result<(Vec<u8>, Size2D<i32>), ()>; pub const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256; macro_rules! handle_potential_webgl_error { @@ -263,6 +264,73 @@ impl WebGLRenderingContext { } } + fn get_image_pixels(&self, + source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) + -> ImagePixelResult { + let source = match source { + Some(s) => s, + None => return Err(()), + }; + + // NOTE: Getting the pixels probably can be short-circuited if some + // parameter is invalid. + // + // Nontheless, since it's the error case, I'm not totally sure the + // complexity is worth it. + let (pixels, size) = match source { + ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::ImageData(image_data) => { + let global = self.global(); + (image_data.get_data_array(&global.r()), image_data.get_size()) + }, + ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLImageElement(image) => { + let img_url = match image.get_url() { + Some(url) => url, + None => return Err(()), + }; + + let window = window_from_node(&*self.canvas); + + let img = match canvas_utils::request_image_from_cache(window.r(), img_url) { + ImageResponse::Loaded(img) => img, + ImageResponse::PlaceholderLoaded(_) | ImageResponse::None | + ImageResponse::MetadataLoaded(_) + => return Err(()), + }; + + let size = Size2D::new(img.width as i32, img.height as i32); + + // TODO(emilio): Validate that the format argument + // is coherent with the image. + // + // RGB8 should be easy to support too + let mut data = match img.format { + PixelFormat::RGBA8 => img.bytes.to_vec(), + _ => unimplemented!(), + }; + + byte_swap(&mut data); + + (data, size) + }, + // TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D, + // but we need to refactor it moving it to `HTMLCanvasElement` and support + // WebGLContext (probably via GetPixels()). + ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLCanvasElement(canvas) => { + let canvas = canvas.r(); + if let Some((mut data, size)) = canvas.fetch_all_data() { + byte_swap(&mut data); + (data, size) + } else { + return Err(()); + } + }, + ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLVideoElement(_rooted_video) + => unimplemented!(), + }; + + return Ok((pixels, size)); + } + fn validate_tex_internal_format(&self, internal_format: u32) -> bool { // GL_INVALID_VALUE is generated if internal_format is not an // accepted format. @@ -281,6 +349,79 @@ impl WebGLRenderingContext { } } + fn validate_tex_format(&self, format: u32) -> bool { + // GL_INVALID_VALUE is generated if internal_format is not an + // accepted format. + match format { + constants::DEPTH_COMPONENT | + constants::ALPHA | + constants::RGB | + constants::RGBA | + constants::LUMINANCE | + constants::LUMINANCE_ALPHA => true, + + _ => { + self.webgl_error(InvalidEnum); + false + }, + } + } + + #[allow(unsafe_code)] + fn validate_tex_image_2d_data(&self, + width: i32, + height: i32, + format: u32, + data_type: u32, + data: Option<*mut JSObject>) + -> Result<i32, ()> { + // TODO(emilio, #10693): Add type-safe wrappers to validations + let (element_size, components_per_element) = match data_type { + constants::UNSIGNED_BYTE => (1, 1), + constants::UNSIGNED_SHORT_5_6_5 => (2, 3), + constants::UNSIGNED_SHORT_5_5_5_1 | + constants::UNSIGNED_SHORT_4_4_4_4 => (2, 4), + _ => unreachable!(), // previously validated + }; + + let components = match format { + constants::DEPTH_COMPONENT => 1, + constants::ALPHA => 1, + constants::LUMINANCE => 1, + constants::LUMINANCE_ALPHA => 2, + constants::RGB => 3, + constants::RGBA => 4, + _ => unreachable!(), // previously validated + }; + + // If data is non-null, the type of pixels must match the type of the + // data to be read. + // If it is UNSIGNED_BYTE, a Uint8Array must be supplied; + // if it is UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4, + // or UNSIGNED_SHORT_5_5_5_1, a Uint16Array must be supplied. + // If the types do not match, an INVALID_OPERATION error is generated. + let received_size = if let Some(data) = data { + if unsafe { array_buffer_view_data_checked::<u16>(data).is_some() } { + 2 + } else if unsafe { array_buffer_view_data_checked::<u8>(data).is_some() } { + 1 + } else { + self.webgl_error(InvalidOperation); + return Err(()); + } + } else { + element_size + }; + + if received_size != element_size { + self.webgl_error(InvalidOperation); + return Err(()); + } + + // NOTE: width and height are positive or zero due to validate() + let expected_byte_length = width * height * element_size * components / components_per_element; + return Ok(expected_byte_length); + } fn validate_tex_image_2d_parameters(&self, target: u32, @@ -307,6 +448,10 @@ impl WebGLRenderingContext { return false; }, } + // Validate format + if !self.validate_tex_format(format) { + return false; + } // Validate internal_format if !self.validate_tex_internal_format(internal_format) { @@ -461,8 +606,8 @@ impl WebGLRenderingContext { width as u32, height as u32, 1, internal_format, - level as u32)); - + level as u32, + Some(data_type))); // TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32, @@ -472,6 +617,71 @@ impl WebGLRenderingContext { .send(CanvasMsg::WebGL(msg)) .unwrap() } + + fn tex_sub_image_2d(&self, + target: u32, + level: i32, + xoffset: i32, + yoffset: i32, + width: i32, + height: i32, + format: u32, + data_type: u32, + pixels: Vec<u8>) { // NB: pixels should NOT be premultipied + // This should be validated before reaching this function + debug_assert!(self.validate_tex_image_2d_parameters(target, level, + format, + width, height, + 0, format, + data_type)); + + let slot = match target { + constants::TEXTURE_2D + => self.bound_texture_2d.get(), + constants::TEXTURE_CUBE_MAP + => self.bound_texture_cube_map.get(), + + _ => return self.webgl_error(InvalidEnum), + }; + + let texture = slot.as_ref().expect("No bound texture found after validation"); + + if format == constants::RGBA && + data_type == constants::UNSIGNED_BYTE && + self.texture_unpacking_settings.get().contains(PREMULTIPLY_ALPHA) { + // TODO(emilio): premultiply here. + } + + // We have already validated level + let face_index = self.face_index_for_target(target).unwrap(); + let image_info = texture.image_info_at_face(face_index, level as u32); + + // GL_INVALID_VALUE is generated if: + // - xoffset or yoffset is less than 0 + // - x offset plus the width is greater than the texture width + // - y offset plus the height is greater than the texture height + if xoffset < 0 || ((xoffset + width) as u32) > image_info.width() || + yoffset < 0 || ((yoffset + height) as u32) > image_info.height() { + return self.webgl_error(InvalidValue); + } + + // Using internal_format() to do this check + // because we are sure format is as same as internal_format. + if format != image_info.internal_format().unwrap() || + data_type != image_info.data_type().unwrap() { + return self.webgl_error(InvalidOperation); + } + + // TODO(emilio): Flip Y axis if necessary here + + // TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested + let msg = WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, + width, height, format, data_type, pixels); + + self.ipc_renderer + .send(CanvasMsg::WebGL(msg)) + .unwrap() + } } impl Drop for WebGLRenderingContext { @@ -889,7 +1099,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { width as u32, height as u32, 1, internal_format, - level as u32)); + level as u32, + None)); let msg = WebGLCommand::CopyTexImage2D(target, level, internal_format, x, y, width, height, border); @@ -1881,7 +2092,6 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { .unwrap() } - #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn TexImage2D(&self, _cx: *mut JSContext, @@ -1904,51 +2114,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return; // Error handled in validate() } - // TODO(emilio, #10693): Add type-safe wrappers to validations - let (element_size, components_per_element) = match data_type { - constants::UNSIGNED_BYTE => (1, 1), - constants::UNSIGNED_SHORT_5_6_5 => (2, 3), - constants::UNSIGNED_SHORT_5_5_5_1 | - constants::UNSIGNED_SHORT_4_4_4_4 => (2, 4), - _ => unreachable!(), // previously validated - }; - - let components = match format { - constants::DEPTH_COMPONENT => 1, - constants::ALPHA => 1, - constants::LUMINANCE => 1, - constants::LUMINANCE_ALPHA => 2, - constants::RGB => 3, - constants::RGBA => 4, - _ => unreachable!(), // previously validated - }; - - // If data is non-null, the type of pixels must match the type of the - // data to be read. - // If it is UNSIGNED_BYTE, a Uint8Array must be supplied; - // if it is UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4, - // or UNSIGNED_SHORT_5_5_5_1, a Uint16Array must be supplied. - // If the types do not match, an INVALID_OPERATION error is generated. - let received_size = if let Some(data) = data { - if unsafe { array_buffer_view_data_checked::<u16>(data).is_some() } { - 2 - } else if unsafe { array_buffer_view_data_checked::<u8>(data).is_some() } { - 1 - } else { - return self.webgl_error(InvalidOperation); - } - } else { - element_size + let expected_byte_length = match self.validate_tex_image_2d_data(width, + height, + format, + data_type, + data) { + Ok(byte_length) => byte_length, + Err(_) => return, }; - if received_size != element_size { - return self.webgl_error(InvalidOperation); - } - - // NOTE: width and height are positive or zero due to validate() - let expected_byte_length = width * height * element_size * components / components_per_element; - - // If data is null, a buffer of sufficient size // initialized to 0 is passed. let buff = if let Some(data) = data { @@ -1976,79 +2150,102 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { format: u32, data_type: u32, source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) { - let source = match source { - Some(s) => s, - None => return, + // Get pixels from image source + let (pixels, size) = match self.get_image_pixels(source) { + Ok((pixels, size)) => (pixels, size), + Err(_) => return, }; + // NB: Border must be zero + if !self.validate_tex_image_2d_parameters(target, level, internal_format, + size.width, size.height, 0, + format, data_type) { + return; // Error handled in validate() + } - // NOTE: Getting the pixels probably can be short-circuited if some - // parameter is invalid. - // - // Nontheless, since it's the error case, I'm not totally sure the - // complexity is worth it. - let (pixels, size) = match source { - ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::ImageData(image_data) => { - let global = self.global(); - (image_data.get_data_array(&global.r()), image_data.get_size()) - }, - ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLImageElement(image) => { - let img_url = match image.get_url() { - Some(url) => url, - None => return, - }; + self.tex_image_2d(target, level, + internal_format, + size.width, size.height, 0, + format, data_type, pixels); + } - let window = window_from_node(&*self.canvas); + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn TexSubImage2D(&self, + _cx: *mut JSContext, + target: u32, + level: i32, + xoffset: i32, + yoffset: i32, + width: i32, + height: i32, + format: u32, + data_type: u32, + data: Option<*mut JSObject>) { + if !self.validate_tex_image_2d_parameters(target, + level, + format, + width, height, + 0, + format, + data_type) { + return; // Error handled in validate() + } - let img = match canvas_utils::request_image_from_cache(window.r(), img_url) { - ImageResponse::Loaded(img) => img, - ImageResponse::PlaceholderLoaded(_) | ImageResponse::None | - ImageResponse::MetadataLoaded(_) - => return, - }; + let expected_byte_length = match self.validate_tex_image_2d_data(width, + height, + format, + data_type, + data) { + Ok(byte_length) => byte_length, + Err(()) => return, + }; - let size = Size2D::new(img.width as i32, img.height as i32); + // If data is null, a buffer of sufficient size + // initialized to 0 is passed. + let buff = if let Some(data) = data { + array_buffer_view_to_vec::<u8>(data) + .expect("Can't reach here without being an ArrayBufferView!") + } else { + vec![0u8; expected_byte_length as usize] + }; - // TODO(emilio): Validate that the format argument - // is coherent with the image. - // - // RGB8 should be easy to support too - let mut data = match img.format { - PixelFormat::RGBA8 => img.bytes.to_vec(), - _ => unimplemented!(), - }; + if expected_byte_length != 0 && + buff.len() != expected_byte_length as usize { + return self.webgl_error(InvalidOperation); + } - byte_swap(&mut data); + self.tex_sub_image_2d(target, level, + xoffset, yoffset, + width, height, + format, data_type, buff); + } - (data, size) - }, - // TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D, - // but we need to refactor it moving it to `HTMLCanvasElement` and support - // WebGLContext (probably via GetPixels()). - ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLCanvasElement(canvas) => { - let canvas = canvas.r(); - if let Some((mut data, size)) = canvas.fetch_all_data() { - byte_swap(&mut data); - (data, size) - } else { - return - } - }, - ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLVideoElement(_rooted_video) - => unimplemented!(), + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn TexSubImage2D_(&self, + target: u32, + level: i32, + xoffset: i32, + yoffset: i32, + format: u32, + data_type: u32, + source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) { + // Get pixels from image source + let (pixels, size) = match self.get_image_pixels(source) { + Ok((pixels, size)) => (pixels, size), + Err(_) => return, }; // NB: Border must be zero - if !self.validate_tex_image_2d_parameters(target, level, internal_format, + if !self.validate_tex_image_2d_parameters(target, level, format, size.width, size.height, 0, format, data_type) { return; // Error handled in validate() } - self.tex_image_2d(target, level, - internal_format, - size.width, size.height, 0, - format, data_type, pixels) + self.tex_sub_image_2d(target, level, + xoffset, yoffset, + size.width, size.height, + format, data_type, pixels); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 diff --git a/components/script/dom/webgltexture.rs b/components/script/dom/webgltexture.rs index ccd05182968..bd9f34515ba 100644 --- a/components/script/dom/webgltexture.rs +++ b/components/script/dom/webgltexture.rs @@ -109,13 +109,15 @@ impl WebGLTexture { height: u32, depth: u32, internal_format: u32, - level: u32) -> WebGLResult<()> { + level: u32, + data_type: Option<u32>) -> WebGLResult<()> { let image_info = ImageInfo { width: width, height: height, depth: depth, internal_format: Some(internal_format), is_initialized: true, + data_type: data_type, }; let face = match target { @@ -274,6 +276,7 @@ impl WebGLTexture { depth: 0, internal_format: base_image_info.internal_format, is_initialized: base_image_info.is_initialized(), + data_type: base_image_info.data_type, }; self.set_image_infos_at_level(level, image_info); @@ -346,6 +349,7 @@ pub struct ImageInfo { depth: u32, internal_format: Option<u32>, is_initialized: bool, + data_type: Option<u32>, } impl ImageInfo { @@ -356,6 +360,7 @@ impl ImageInfo { depth: 0, internal_format: None, is_initialized: false, + data_type: None, } } @@ -371,6 +376,10 @@ impl ImageInfo { self.internal_format } + pub fn data_type(&self) -> Option<u32> { + self.data_type + } + fn is_power_of_two(&self) -> bool { self.width.is_power_of_two() && self.height.is_power_of_two() && self.depth.is_power_of_two() } diff --git a/components/script/dom/webidls/BrowserElement.webidl b/components/script/dom/webidls/BrowserElement.webidl index ae3b8e310e3..9351cc9377a 100644 --- a/components/script/dom/webidls/BrowserElement.webidl +++ b/components/script/dom/webidls/BrowserElement.webidl @@ -159,16 +159,16 @@ interface BrowserElementPrivileged { // unsigned long count, // unsigned long modifiers); - [Func="Window::global_is_mozbrowser", Throws] + [Func="::dom::window::Window::global_is_mozbrowser", Throws] void goBack(); - [Func="Window::global_is_mozbrowser", Throws] + [Func="::dom::window::Window::global_is_mozbrowser", Throws] void goForward(); - [Func="Window::global_is_mozbrowser", Throws] + [Func="::dom::window::Window::global_is_mozbrowser", Throws] void reload(optional boolean hardReload = false); - [Func="Window::global_is_mozbrowser", Throws] + [Func="::dom::window::Window::global_is_mozbrowser", Throws] void stop(); //[Throws, diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl index 48aeed7fbbb..d09f4846c98 100644 --- a/components/script/dom/webidls/Element.webidl +++ b/components/script/dom/webidls/Element.webidl @@ -84,14 +84,22 @@ partial interface Element { DOMRectList getClientRects(); DOMRect getBoundingClientRect(); + [Func="::script_can_initiate_scroll"] void scroll(optional ScrollToOptions options); + [Func="::script_can_initiate_scroll"] void scroll(unrestricted double x, unrestricted double y); + [Func="::script_can_initiate_scroll"] void scrollTo(optional ScrollToOptions options); + [Func="::script_can_initiate_scroll"] void scrollTo(unrestricted double x, unrestricted double y); + [Func="::script_can_initiate_scroll"] void scrollBy(optional ScrollToOptions options); + [Func="::script_can_initiate_scroll"] void scrollBy(unrestricted double x, unrestricted double y); + [Func="::script_can_initiate_scroll"] attribute unrestricted double scrollTop; + [Func="::script_can_initiate_scroll"] attribute unrestricted double scrollLeft; readonly attribute long scrollWidth; readonly attribute long scrollHeight; diff --git a/components/script/dom/webidls/HTMLIFrameElement.webidl b/components/script/dom/webidls/HTMLIFrameElement.webidl index 65ccbe84048..5fe4ef7a661 100644 --- a/components/script/dom/webidls/HTMLIFrameElement.webidl +++ b/components/script/dom/webidls/HTMLIFrameElement.webidl @@ -7,14 +7,14 @@ interface HTMLIFrameElement : HTMLElement { attribute DOMString src; // attribute DOMString srcdoc; // attribute DOMString name; - attribute DOMString sandbox; + [SameObject, PutForwards=value] + readonly attribute DOMTokenList sandbox; // attribute boolean seamless; // attribute boolean allowFullscreen; attribute DOMString width; attribute DOMString height; readonly attribute Document? contentDocument; - //readonly attribute WindowProxy? contentWindow; - readonly attribute Window? contentWindow; + readonly attribute WindowProxy? contentWindow; // also has obsolete members }; @@ -31,7 +31,7 @@ partial interface HTMLIFrameElement { }; partial interface HTMLIFrameElement { - [Func="Window::global_is_mozbrowser"] + [Func="::dom::window::Window::global_is_mozbrowser"] attribute boolean mozbrowser; }; diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl index 3ed1c8cdb31..679fedf354b 100644 --- a/components/script/dom/webidls/WebGLRenderingContext.webidl +++ b/components/script/dom/webidls/WebGLRenderingContext.webidl @@ -646,11 +646,11 @@ interface WebGLRenderingContextBase void texParameterf(GLenum target, GLenum pname, GLfloat param); void texParameteri(GLenum target, GLenum pname, GLint param); - //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLsizei width, GLsizei height, - // GLenum format, GLenum type, ArrayBufferView? pixels); - //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLenum format, GLenum type, TexImageSource? source); // May throw DOMException + void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, + GLenum format, GLenum type, optional object data); + void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLenum format, GLenum type, TexImageSource? source); // May throw DOMException void uniform1f(WebGLUniformLocation? location, GLfloat x); //void uniform1fv(WebGLUniformLocation? location, Float32Array v); diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index b263d53bb7b..1bf0f7f2c50 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -6,10 +6,8 @@ [PrimaryGlobal] /*sealed*/ interface Window : EventTarget { // the current browsing context - //[Unforgeable] readonly attribute WindowProxy window; - //[Replaceable] readonly attribute WindowProxy self; - readonly attribute Window window; - [BinaryName="Self_"] readonly attribute Window self; + [Unforgeable] readonly attribute WindowProxy window; + [BinaryName="Self_", Replaceable] readonly attribute WindowProxy self; [Unforgeable] readonly attribute Document document; // attribute DOMString name; [/*PutForwards=href, */Unforgeable] readonly attribute Location location; @@ -28,14 +26,11 @@ //void blur(); // other browsing contexts - //[Replaceable] readonly attribute WindowProxy frames; - readonly attribute Window frames; + [Replaceable] readonly attribute WindowProxy frames; //[Replaceable] readonly attribute unsigned long length; - //[Unforgeable] readonly attribute WindowProxy top; - readonly attribute Window top; + [Unforgeable] readonly attribute WindowProxy top; // attribute any opener; - //readonly attribute WindowProxy parent; - readonly attribute Window parent; + readonly attribute WindowProxy parent; readonly attribute Element? frameElement; //WindowProxy open(optional DOMString url = "about:blank", optional DOMString target = "_blank", // optional DOMString features = "", optional boolean replace = false); @@ -65,6 +60,9 @@ Window implements GlobalEventHandlers; Window implements WindowEventHandlers; +[NoInterfaceObject] +interface WindowProxy {}; + // https://html.spec.whatwg.org/multipage/#timers [NoInterfaceObject/*, Exposed=Window,Worker*/] interface WindowTimers { @@ -138,11 +136,17 @@ partial interface Window { readonly attribute long pageXOffset; readonly attribute long scrollY; readonly attribute long pageYOffset; + [Func="::script_can_initiate_scroll"] void scroll(optional ScrollToOptions options); + [Func="::script_can_initiate_scroll"] void scroll(unrestricted double x, unrestricted double y); + [Func="::script_can_initiate_scroll"] void scrollTo(optional ScrollToOptions options); + [Func="::script_can_initiate_scroll"] void scrollTo(unrestricted double x, unrestricted double y); + [Func="::script_can_initiate_scroll"] void scrollBy(optional ScrollToOptions options); + [Func="::script_can_initiate_scroll"] void scrollBy(unrestricted double x, unrestricted double y); // client diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index c8d327897d2..34f7c1acdb0 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -12,6 +12,7 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::EventHandlerBinding::OnBeforeUnloadEventHandlerNonNull; use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull; use dom::bindings::codegen::Bindings::FunctionBinding::Function; +use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions}; use dom::bindings::codegen::Bindings::WindowBinding::{self, FrameRequestCallback, WindowMethods}; use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception}; @@ -68,7 +69,7 @@ use script_traits::{ScriptMsg as ConstellationMsg, TimerEventRequest, TimerSourc use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::cell::Cell; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::default::Default; use std::ffi::CString; use std::io::{Write, stderr, stdout}; @@ -264,6 +265,9 @@ pub struct Window { error_reporter: CSSErrorReporter, + /// A list of scroll offsets for each scrollable element. + scroll_offsets: DOMRefCell<HashMap<UntrustedNodeAddress, Point2D<f32>>>, + #[ignore_heap_size_of = "Defined in ipc-channel"] panic_chan: IpcSender<PanicMsg>, } @@ -354,6 +358,13 @@ impl Window { pub fn css_error_reporter(&self) -> Box<ParseErrorReporter + Send> { self.error_reporter.clone() } + + /// Sets a new list of scroll offsets. + /// + /// This is called when layout gives us new ones and WebRender is in use. + pub fn set_scroll_offsets(&self, offsets: HashMap<UntrustedNodeAddress, Point2D<f32>>) { + *self.scroll_offsets.borrow_mut() = offsets + } } #[cfg(any(target_os = "macos", target_os = "linux"))] @@ -558,32 +569,35 @@ impl WindowMethods for Window { } // https://html.spec.whatwg.org/multipage/#dom-window - fn Window(&self) -> Root<Window> { - Root::from_ref(self) + fn Window(&self) -> Root<BrowsingContext> { + self.browsing_context() } // https://html.spec.whatwg.org/multipage/#dom-self - fn Self_(&self) -> Root<Window> { - self.Window() + fn Self_(&self) -> Root<BrowsingContext> { + self.browsing_context() } // https://html.spec.whatwg.org/multipage/#dom-frames - fn Frames(&self) -> Root<Window> { - self.Window() + fn Frames(&self) -> Root<BrowsingContext> { + self.browsing_context() } // https://html.spec.whatwg.org/multipage/#dom-parent - fn Parent(&self) -> Root<Window> { - self.parent().unwrap_or(self.Window()) + fn Parent(&self) -> Root<BrowsingContext> { + match self.parent() { + Some(window) => window.browsing_context(), + None => self.Window() + } } // https://html.spec.whatwg.org/multipage/#dom-top - fn Top(&self) -> Root<Window> { - let mut window = self.Window(); + fn Top(&self) -> Root<BrowsingContext> { + let mut window = Root::from_ref(self); while let Some(parent) = window.parent() { window = parent; } - window + window.browsing_context() } // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/ @@ -1240,7 +1254,28 @@ impl Window { self.layout_rpc.node_overflow().0.unwrap() } - pub fn scroll_offset_query(&self, node: TrustedNodeAddress) -> Point2D<f32> { + pub fn scroll_offset_query(&self, node: &Node) -> Point2D<f32> { + // WebRender always keeps the scroll offsets up to date and stored here in the window. So, + // if WR is in use, all we need to do is to check our list of scroll offsets and return the + // result. + if opts::get().use_webrender { + let mut node = Root::from_ref(node); + loop { + if let Some(scroll_offset) = self.scroll_offsets + .borrow() + .get(&node.to_untrusted_node_address()) { + return *scroll_offset + } + node = match node.GetParentNode() { + Some(node) => node, + None => break, + } + } + let offset = self.current_viewport.get().origin; + return Point2D::new(offset.x.to_f32_px(), offset.y.to_f32_px()) + } + + let node = node.to_trusted_node_address(); if !self.reflow(ReflowGoal::ForScriptQuery, ReflowQueryType::NodeLayerIdQuery(node), ReflowReason::Query) { @@ -1639,6 +1674,7 @@ impl Window { webdriver_script_chan: DOMRefCell::new(None), ignore_further_async_events: Arc::new(AtomicBool::new(false)), error_reporter: error_reporter, + scroll_offsets: DOMRefCell::new(HashMap::new()), panic_chan: panic_chan, }; diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 318d3bbc1ec..785a5928c9f 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -2,8 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use cors::CORSResponse; -use cors::{AsyncCORSResponseListener, CORSRequest, RequestMode, allow_cross_origin_request}; use document_loader::DocumentLoader; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods; @@ -35,27 +33,28 @@ use encoding::label::encoding_from_whatwg_label; use encoding::types::{DecoderTrap, EncoderTrap, Encoding, EncodingRef}; use euclid::length::Length; use hyper::header::Headers; -use hyper::header::{Accept, ContentLength, ContentType, qitem}; +use hyper::header::{ContentLength, ContentType}; use hyper::http::RawStatus; use hyper::method::Method; -use hyper::mime::{self, Mime}; +use hyper::mime::{self, Mime, Attr as MimeAttr, Value as MimeValue}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use js::jsapi::JS_ClearPendingException; use js::jsapi::{JSContext, JS_ParseJSON, RootedValue}; use js::jsval::{JSVal, NullValue, UndefinedValue}; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; -use net_traits::CoreResourceMsg::Load; +use net_traits::CoreResourceMsg::Fetch; +use net_traits::request::{CredentialsMode, Destination, RequestInit, RequestMode}; use net_traits::trim_http_whitespace; -use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError, RequestSource}; -use net_traits::{LoadConsumer, LoadContext, LoadData, ResourceCORSData, CoreResourceThread, LoadOrigin}; +use net_traits::{CoreResourceThread, LoadOrigin}; +use net_traits::{FetchResponseListener, Metadata, NetworkError, RequestSource}; use network_listener::{NetworkListener, PreInvoke}; use parse::html::{ParseContext, parse_html}; use parse::xml::{self, parse_xml}; use script_runtime::ScriptChan; use std::ascii::AsciiExt; use std::borrow::ToOwned; -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::default::Default; use std::str; use std::sync::{Arc, Mutex}; @@ -82,7 +81,6 @@ pub struct GenerationId(u32); struct XHRContext { xhr: TrustedXHRAddress, gen_id: GenerationId, - cors_request: Option<CORSRequest>, buf: DOMRefCell<Vec<u8>>, sync_status: DOMRefCell<Option<ErrorResult>>, } @@ -142,7 +140,6 @@ pub struct XMLHttpRequest { request_body_len: Cell<usize>, sync: Cell<bool>, upload_complete: Cell<bool>, - upload_events: Cell<bool>, send_flag: Cell<bool>, timeout_cancel: DOMRefCell<Option<OneshotTimerHandle>>, @@ -187,7 +184,6 @@ impl XMLHttpRequest { request_body_len: Cell::new(0), sync: Cell::new(false), upload_complete: Cell::new(false), - upload_events: Cell::new(false), send_flag: Cell::new(false), timeout_cancel: DOMRefCell::new(None), @@ -216,75 +212,40 @@ impl XMLHttpRequest { } } - fn check_cors(context: Arc<Mutex<XHRContext>>, - load_data: LoadData, - req: CORSRequest, - script_chan: Box<ScriptChan + Send>, - core_resource_thread: CoreResourceThread) { - struct CORSContext { - xhr: Arc<Mutex<XHRContext>>, - load_data: RefCell<Option<LoadData>>, - req: CORSRequest, - script_chan: Box<ScriptChan + Send>, - core_resource_thread: CoreResourceThread, - } - - impl AsyncCORSResponseListener for CORSContext { - fn response_available(&self, response: CORSResponse) { - if response.network_error { - let mut context = self.xhr.lock().unwrap(); - let xhr = context.xhr.root(); - xhr.process_partial_response(XHRProgress::Errored(context.gen_id, Error::Network)); - *context.sync_status.borrow_mut() = Some(Err(Error::Network)); - return; - } - - let mut load_data = self.load_data.borrow_mut().take().unwrap(); - load_data.cors = Some(ResourceCORSData { - preflight: self.req.preflight_flag, - origin: self.req.origin.clone() - }); - - XMLHttpRequest::initiate_async_xhr(self.xhr.clone(), self.script_chan.clone(), - self.core_resource_thread.clone(), load_data); - } - } - - let cors_context = CORSContext { - xhr: context, - load_data: RefCell::new(Some(load_data)), - req: req.clone(), - script_chan: script_chan.clone(), - core_resource_thread: core_resource_thread, - }; - - req.http_fetch_async(box cors_context, script_chan); - } - fn initiate_async_xhr(context: Arc<Mutex<XHRContext>>, script_chan: Box<ScriptChan + Send>, core_resource_thread: CoreResourceThread, - load_data: LoadData) { - impl AsyncResponseListener for XHRContext { - fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) { - let xhr = self.xhr.root(); - let rv = xhr.process_headers_available(self.cors_request.clone(), - self.gen_id, - metadata); - if rv.is_err() { + init: RequestInit) { + impl FetchResponseListener for XHRContext { + fn process_request_body(&mut self) { + // todo + } + fn process_request_eof(&mut self) { + // todo + } + fn process_response(&mut self, metadata: Result<Metadata, NetworkError>) { + let xhr = self.xhr.root(); + let rv = xhr.process_headers_available(self.gen_id, + metadata); + if rv.is_err() { + *self.sync_status.borrow_mut() = Some(rv); + } + } + fn process_response_chunk(&mut self, mut chunk: Vec<u8>) { + self.buf.borrow_mut().append(&mut chunk); + self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone()); + } + fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + let rv = match response { + Ok(()) => { + self.xhr.root().process_response_complete(self.gen_id, Ok(())) + } + Err(e) => { + self.xhr.root().process_response_complete(self.gen_id, Err(e)) + } + }; *self.sync_status.borrow_mut() = Some(rv); } - } - - fn data_available(&mut self, payload: Vec<u8>) { - self.buf.borrow_mut().extend_from_slice(&payload); - self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone()); - } - - fn response_complete(&mut self, status: Result<(), NetworkError>) { - let rv = self.xhr.root().process_response_complete(self.gen_id, status); - *self.sync_status.borrow_mut() = Some(rv); - } } impl PreInvoke for XHRContext { @@ -298,13 +259,10 @@ impl XMLHttpRequest { context: context, script_chan: script_chan, }; - let response_target = AsyncResponseTarget { - sender: action_sender, - }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { - listener.notify(message.to().unwrap()); + listener.notify_fetch(message.to().unwrap()); }); - core_resource_thread.send(Load(load_data, LoadConsumer::Listener(response_target), None)).unwrap(); + core_resource_thread.send(Fetch(init, action_sender)).unwrap(); } } @@ -563,12 +521,15 @@ impl XMLHttpRequestMethods for XMLHttpRequest { Method::Get | Method::Head => None, _ => data }; - // Step 4 + // Step 4 (first half) let extracted = data.as_ref().map(|d| d.extract()); + self.request_body_len.set(extracted.as_ref().map_or(0, |e| e.0.len())); + // todo preserved headers? + // Step 6 - self.upload_events.set(false); + self.upload_complete.set(false); // Step 7 self.upload_complete.set(match extracted { None => true, @@ -580,11 +541,6 @@ impl XMLHttpRequestMethods for XMLHttpRequest { // Step 9 if !self.sync.get() { - let event_target = self.upload.upcast::<EventTarget>(); - if event_target.has_handlers() { - self.upload_events.set(true); - } - // If one of the event handlers below aborts the fetch by calling // abort or open we will need the current generation id to detect it. // Substep 1 @@ -604,67 +560,108 @@ impl XMLHttpRequestMethods for XMLHttpRequest { } // Step 5 - let global = self.global(); + //TODO - set referrer_policy/referrer_url in request + let has_handlers = self.upload.upcast::<EventTarget>().has_handlers(); + let credentials_mode = if self.with_credentials.get() { + CredentialsMode::Include + } else { + CredentialsMode::CredentialsSameOrigin + }; + let use_url_credentials = if let Some(ref url) = *self.request_url.borrow() { + !url.username().is_empty() || url.password().is_some() + } else { + unreachable!() + }; - let mut load_data = - LoadData::new(LoadContext::Browsing, - self.request_url.borrow().clone().unwrap(), - self); + let bypass_cross_origin_check = { + // We want to be able to do cross-origin requests in browser.html. + // If the XHR happens in a top level window and the mozbrowser + // preference is enabled, we allow bypassing the CORS check. + // This is a temporary measure until we figure out Servo privilege + // story. See https://github.com/servo/servo/issues/9582 + if let GlobalRoot::Window(win) = self.global() { + let is_root_pipeline = win.parent_info().is_none(); + let is_mozbrowser_enabled = mozbrowser_enabled(); + is_root_pipeline && is_mozbrowser_enabled + } else { + false + } + }; - if load_data.url.origin().ne(&global.r().get_url().origin()) { - load_data.credentials_flag = self.WithCredentials(); - } - load_data.data = extracted.as_ref().map(|e| e.0.clone()); + let mut request = RequestInit { + method: self.request_method.borrow().clone(), + url: self.request_url.borrow().clone().unwrap(), + headers: (*self.request_headers.borrow()).clone(), + unsafe_request: true, + same_origin_data: true, + // XXXManishearth figure out how to avoid this clone + body: extracted.as_ref().map(|e| e.0.clone()), + // XXXManishearth actually "subresource", but it doesn't exist + // https://github.com/whatwg/xhr/issues/71 + destination: Destination::None, + synchronous: self.sync.get(), + mode: RequestMode::CORSMode, + use_cors_preflight: has_handlers, + credentials_mode: credentials_mode, + use_url_credentials: use_url_credentials, + origin: self.global().r().get_url(), + referer_url: self.referrer_url.clone(), + referrer_policy: self.referrer_policy.clone(), + }; - // XHR spec differs from http, and says UTF-8 should be in capitals, - // instead of "utf-8", which is what Hyper defaults to. So not - // using content types provided by Hyper. - let n = "content-type"; - match extracted { - Some((_, Some(ref content_type))) => - load_data.headers.set_raw(n.to_owned(), vec![content_type.bytes().collect()]), - _ => (), + if bypass_cross_origin_check { + request.mode = RequestMode::Navigate; } - load_data.preserved_headers = (*self.request_headers.borrow()).clone(); + // step 4 (second half) + match extracted { + Some((_, ref content_type)) => { + // this should handle Document bodies too, not just BodyInit + let encoding = if let Some(BodyInit::String(_)) = data { + // XHR spec differs from http, and says UTF-8 should be in capitals, + // instead of "utf-8", which is what Hyper defaults to. So not + // using content types provided by Hyper. + Some(MimeValue::Ext("UTF-8".to_string())) + } else { + None + }; - if !load_data.preserved_headers.has::<Accept>() { - let mime = Mime(mime::TopLevel::Star, mime::SubLevel::Star, vec![]); - load_data.preserved_headers.set(Accept(vec![qitem(mime)])); - } + let mut content_type_set = false; + if let Some(ref ct) = *content_type { + if !request.headers.has::<ContentType>() { + request.headers.set_raw("content-type", vec![ct.bytes().collect()]); + content_type_set = true; + } + } - load_data.method = (*self.request_method.borrow()).clone(); + if !content_type_set { + let ct = request.headers.get::<ContentType>().map(|x| x.clone()); + if let Some(mut ct) = ct { + if let Some(encoding) = encoding { + for param in &mut (ct.0).2 { + if param.0 == MimeAttr::Charset { + if !param.0.as_str().eq_ignore_ascii_case(encoding.as_str()) { + *param = (MimeAttr::Charset, encoding.clone()); + } + } + } + } + // remove instead of mutate in place + // https://github.com/hyperium/hyper/issues/821 + request.headers.remove_raw("content-type"); + request.headers.set(ct); + } + } - // CORS stuff - let global = self.global(); - let referer_url = self.global().r().get_url(); - let mode = if self.upload_events.get() { - RequestMode::ForcedPreflight - } else { - RequestMode::CORS - }; - let mut combined_headers = load_data.headers.clone(); - combined_headers.extend(load_data.preserved_headers.iter()); - let cors_request = CORSRequest::maybe_new(referer_url.clone(), - load_data.url.clone(), - mode, - load_data.method.clone(), - combined_headers, - true); - match cors_request { - Ok(None) => { - let bytes = referer_url[..Position::AfterPath].as_bytes().to_vec(); - self.request_headers.borrow_mut().set_raw("Referer".to_owned(), vec![bytes]); - }, - Ok(Some(ref req)) => self.insert_trusted_header("origin".to_owned(), - req.origin.to_string()), - _ => {} + } + _ => (), } - debug!("request_headers = {:?}", *self.request_headers.borrow()); + debug!("request.headers = {:?}", request.headers); self.fetch_time.set(time::now().to_timespec().sec); - let rv = self.fetch(load_data, cors_request, global.r()); + + let rv = self.fetch(request, self.global().r()); // Step 10 if self.sync.get() { return rv; @@ -879,7 +876,7 @@ impl XMLHttpRequest { event.fire(self.upcast()); } - fn process_headers_available(&self, cors_request: Option<CORSRequest>, + fn process_headers_available(&self, gen_id: GenerationId, metadata: Result<Metadata, NetworkError>) -> Result<(), Error> { let metadata = match metadata { @@ -890,35 +887,6 @@ impl XMLHttpRequest { }, }; - let bypass_cross_origin_check = { - // We want to be able to do cross-origin requests in browser.html. - // If the XHR happens in a top level window and the mozbrowser - // preference is enabled, we allow bypassing the CORS check. - // This is a temporary measure until we figure out Servo privilege - // story. See https://github.com/servo/servo/issues/9582 - if let GlobalRoot::Window(win) = self.global() { - let is_root_pipeline = win.parent_info().is_none(); - let is_mozbrowser_enabled = mozbrowser_enabled(); - is_root_pipeline && is_mozbrowser_enabled - } else { - false - } - }; - - if !bypass_cross_origin_check { - if let Some(ref req) = cors_request { - match metadata.headers { - Some(ref h) if allow_cross_origin_request(req, h) => {}, - _ => { - self.process_partial_response(XHRProgress::Errored(gen_id, Error::Network)); - return Err(Error::Network); - } - } - } - } else { - debug!("Bypassing cross origin check"); - } - *self.response_url.borrow_mut() = metadata.final_url[..Position::AfterQuery].to_owned(); // XXXManishearth Clear cache entries in case of a network error @@ -1318,25 +1286,12 @@ impl XMLHttpRequest { } fn fetch(&self, - load_data: LoadData, - cors_request: Result<Option<CORSRequest>, ()>, + init: RequestInit, global: GlobalRef) -> ErrorResult { - let cors_request = match cors_request { - Err(_) => { - // Happens in case of unsupported cross-origin URI schemes. - // Supported schemes are http, https, data, and about. - self.process_partial_response(XHRProgress::Errored( - self.generation_id.get(), Error::Network)); - return Err(Error::Network); - } - Ok(req) => req, - }; - let xhr = Trusted::new(self); let context = Arc::new(Mutex::new(XHRContext { xhr: xhr, - cors_request: cors_request.clone(), gen_id: self.generation_id.get(), buf: DOMRefCell::new(vec!()), sync_status: DOMRefCell::new(None), @@ -1350,13 +1305,8 @@ impl XMLHttpRequest { }; let core_resource_thread = global.core_resource_thread(); - if let Some(req) = cors_request { - XMLHttpRequest::check_cors(context.clone(), load_data, req.clone(), - script_chan.clone(), core_resource_thread); - } else { - XMLHttpRequest::initiate_async_xhr(context.clone(), script_chan, - core_resource_thread, load_data); - } + XMLHttpRequest::initiate_async_xhr(context.clone(), script_chan, + core_resource_thread, init); if let Some(script_port) = script_port { loop { diff --git a/components/script/lib.rs b/components/script/lib.rs index 50b0c47ac6b..2f76a112c0a 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -79,7 +79,6 @@ extern crate style; extern crate time; #[cfg(any(target_os = "macos", target_os = "linux"))] extern crate tinyfiledialogs; -extern crate unicase; extern crate url; #[macro_use] extern crate util; @@ -91,7 +90,6 @@ extern crate xml5ever; mod blob_url_store; pub mod bluetooth_blacklist; pub mod clipboard_provider; -pub mod cors; mod devtools; pub mod document_loader; #[macro_use] @@ -112,8 +110,9 @@ mod unpremultiplytable; mod webdriver_handlers; use dom::bindings::codegen::RegisterBindings; -use js::jsapi::SetDOMProxyInformation; +use js::jsapi::{Handle, JSContext, JSObject, SetDOMProxyInformation}; use std::ptr; +use util::opts; #[cfg(target_os = "linux")] #[allow(unsafe_code)] @@ -168,3 +167,14 @@ pub fn init() { perform_platform_specific_initialization(); } + +/// FIXME(pcwalton): Currently WebRender cannot handle DOM-initiated scrolls. Remove this when it +/// can. See PR #11680 for details. +/// +/// This function is only marked `unsafe` because the `[Func=foo]` WebIDL attribute requires it. It +/// shouldn't actually do anything unsafe. +#[allow(unsafe_code)] +pub unsafe fn script_can_initiate_scroll(_: *mut JSContext, _: Handle<*mut JSObject>) -> bool { + !opts::get().use_webrender +} + diff --git a/components/script/network_listener.rs b/components/script/network_listener.rs index cff0723d277..13aa26c25e4 100644 --- a/components/script/network_listener.rs +++ b/components/script/network_listener.rs @@ -2,7 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use net_traits::{AsyncResponseListener, ResponseAction}; +use net_traits::{Action, AsyncResponseListener, FetchResponseListener}; +use net_traits::{FetchResponseMsg, ResponseAction}; use script_runtime::ScriptThreadEventCategory::NetworkEvent; use script_runtime::{CommonScriptMsg, ScriptChan}; use script_thread::Runnable; @@ -10,13 +11,13 @@ use std::sync::{Arc, Mutex}; /// An off-thread sink for async network event runnables. All such events are forwarded to /// a target thread, where they are invoked on the provided context object. -pub struct NetworkListener<T: AsyncResponseListener + PreInvoke + Send + 'static> { - pub context: Arc<Mutex<T>>, +pub struct NetworkListener<Listener: PreInvoke + Send + 'static> { + pub context: Arc<Mutex<Listener>>, pub script_chan: Box<ScriptChan + Send>, } -impl<T: AsyncResponseListener + PreInvoke + Send + 'static> NetworkListener<T> { - pub fn notify(&self, action: ResponseAction) { +impl<Listener: PreInvoke + Send + 'static> NetworkListener<Listener> { + pub fn notify<A: Action<Listener> + Send + 'static>(&self, action: A) { if let Err(err) = self.script_chan.send(CommonScriptMsg::RunnableMsg(NetworkEvent, box ListenerRunnable { context: self.context.clone(), action: action, @@ -26,6 +27,20 @@ impl<T: AsyncResponseListener + PreInvoke + Send + 'static> NetworkListener<T> { } } +// helps type inference +impl<Listener: AsyncResponseListener + PreInvoke + Send + 'static> NetworkListener<Listener> { + pub fn notify_action(&self, action: ResponseAction) { + self.notify(action); + } +} + +// helps type inference +impl<Listener: FetchResponseListener + PreInvoke + Send + 'static> NetworkListener<Listener> { + pub fn notify_fetch(&self, action: FetchResponseMsg) { + self.notify(action); + } +} + /// A gating mechanism that runs before invoking the runnable on the target thread. /// If the `should_invoke` method returns false, the runnable is discarded without /// being invoked. @@ -36,13 +51,13 @@ pub trait PreInvoke { } /// A runnable for moving the async network events between threads. -struct ListenerRunnable<T: AsyncResponseListener + PreInvoke + Send> { - context: Arc<Mutex<T>>, - action: ResponseAction, +struct ListenerRunnable<A: Action<Listener> + Send + 'static, Listener: PreInvoke + Send> { + context: Arc<Mutex<Listener>>, + action: A, } -impl<T: AsyncResponseListener + PreInvoke + Send> Runnable for ListenerRunnable<T> { - fn handler(self: Box<ListenerRunnable<T>>) { +impl<A: Action<Listener> + Send + 'static, Listener: PreInvoke + Send> Runnable for ListenerRunnable<A, Listener> { + fn handler(self: Box<ListenerRunnable<A, Listener>>) { let this = *self; let mut context = this.context.lock().unwrap(); if context.should_invoke() { diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index eec1853239e..8cf97710bc7 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -85,11 +85,12 @@ use script_traits::{CompositorEvent, ConstellationControlMsg, EventResult}; use script_traits::{InitialScriptState, MouseButton, MouseEventType, MozBrowserEvent}; use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg}; use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSource}; -use script_traits::{TouchEventType, TouchId}; +use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress}; use std::borrow::ToOwned; use std::cell::{Cell, RefCell}; use std::collections::{HashMap, HashSet}; use std::option::Option; +use std::ptr; use std::rc::Rc; use std::result::Result; use std::sync::atomic::{Ordering, AtomicBool}; @@ -728,9 +729,9 @@ impl ScriptThread { self.handle_viewport(id, rect); }) } - FromConstellation(ConstellationControlMsg::SetScrollState(id, scroll_offset)) => { + FromConstellation(ConstellationControlMsg::SetScrollState(id, scroll_state)) => { self.profile_event(ScriptThreadEventCategory::SetScrollState, || { - self.handle_set_scroll_state(id, &scroll_offset); + self.handle_set_scroll_state(id, &scroll_state); }) } FromConstellation(ConstellationControlMsg::TickAllAnimations( @@ -1087,7 +1088,7 @@ impl ScriptThread { load.window_size = Some(size); return; } - panic!("resize sent to nonexistent pipeline"); + warn!("resize sent to nonexistent pipeline"); } fn handle_viewport(&self, id: PipelineId, rect: Rect<f32>) { @@ -1110,17 +1111,31 @@ impl ScriptThread { panic!("Page rect message sent to nonexistent pipeline"); } - fn handle_set_scroll_state(&self, id: PipelineId, scroll_state: &Point2D<f32>) { - let context = self.browsing_context.get(); - if let Some(context) = context { - if let Some(inner_context) = context.find(id) { - let window = inner_context.active_window(); - window.update_viewport_for_scroll(-scroll_state.x, -scroll_state.y); - return + fn handle_set_scroll_state(&self, + id: PipelineId, + scroll_states: &[(UntrustedNodeAddress, Point2D<f32>)]) { + let window = match self.browsing_context.get() { + Some(context) => { + match context.find(id) { + Some(inner_context) => inner_context.active_window(), + None => { + panic!("Set scroll state message sent to nonexistent pipeline: {:?}", id) + } + } } - } + None => panic!("Set scroll state message sent to nonexistent pipeline: {:?}", id), + }; - panic!("Set scroll state message message sent to nonexistent pipeline: {:?}", id); + let mut scroll_offsets = HashMap::new(); + for &(node_address, ref scroll_offset) in scroll_states { + if node_address == UntrustedNodeAddress(ptr::null()) { + window.update_viewport_for_scroll(-scroll_offset.x, -scroll_offset.y); + } else { + scroll_offsets.insert(node_address, + Point2D::new(-scroll_offset.x, -scroll_offset.y)); + } + } + window.set_scroll_offsets(scroll_offsets) } fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) { @@ -1947,7 +1962,7 @@ impl ScriptThread { script_chan: self.chan.clone(), }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { - listener.notify(message.to().unwrap()); + listener.notify_action(message.to().unwrap()); }); let response_target = AsyncResponseTarget { sender: action_sender, diff --git a/components/script/timers.rs b/components/script/timers.rs index 53113e06682..504dbb8aaaf 100644 --- a/components/script/timers.rs +++ b/components/script/timers.rs @@ -176,7 +176,10 @@ impl OneshotTimers { let base_time = self.base_time(); // Since the event id was the expected one, at least one timer should be due. - assert!(base_time >= self.timers.borrow().last().unwrap().scheduled_for); + if base_time < self.timers.borrow().last().unwrap().scheduled_for { + warn!("Unexpected timing!"); + return; + } // select timers to run to prevent firing timers // that were installed during fire of another timer diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 910ffa6c426..3c9d1ffcfb2 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -6,7 +6,6 @@ use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclar use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods; -use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; @@ -105,7 +104,7 @@ pub fn handle_get_frame_id(context: &BrowsingContext, match find_node_by_unique_id(context, pipeline, x) { Some(ref node) => { match node.downcast::<HTMLIFrameElement>() { - Some(ref elem) => Ok(elem.GetContentWindow()), + Some(ref elem) => Ok(elem.get_content_window()), None => Err(()) } }, |