diff --git a/components/style/gecko_bindings/sugar/refptr.rs b/components/style/gecko_bindings/sugar/refptr.rs index 0d8fb817a85..76a7021ef5b 100644 --- a/components/style/gecko_bindings/sugar/refptr.rs +++ b/components/style/gecko_bindings/sugar/refptr.rs @@ -312,3 +312,9 @@ impl_threadsafe_refcount!( Gecko_AddRefSharedFontListArbitraryThread, Gecko_ReleaseSharedFontListArbitraryThread ); + +impl_threadsafe_refcount!( + ::gecko_bindings::structs::SheetLoadDataHolder, + Gecko_AddRefSheetLoadDataHolderArbitraryThread, + Gecko_ReleaseSheetLoadDataHolderArbitraryThread +); diff --git a/components/style/stylesheets/import_rule.rs b/components/style/stylesheets/import_rule.rs index fb50ec69732..576cb31eff3 100644 --- a/components/style/stylesheets/import_rule.rs +++ b/components/style/stylesheets/import_rule.rs @@ -171,10 +171,9 @@ pub struct ImportRule { /// The `` this `@import` rule is loading. pub url: CssUrl, - /// The stylesheet is always present. - /// - /// It contains an empty list of rules and namespace set that is updated - /// when it loads. + /// The stylesheet is always present. However, in the case of gecko async + /// parsing, we don't actually have a Gecko sheet at first, and so the + /// ImportSheet just has stub behavior until it appears. pub stylesheet: ImportSheet, /// The line and column of the rule's source code. diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 8c99a5c5979..77b46fb0ee1 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -6,6 +6,7 @@ use cssparser::{ParseErrorKind, Parser, ParserInput, SourceLocation}; use cssparser::ToCss as ParserToCss; use env_logger::Builder; use malloc_size_of::MallocSizeOfOps; +use nsstring::nsCString; use selectors::{NthIndexCache, SelectorList}; use selectors::matching::{MatchingContext, MatchingMode, matches_selector}; use servo_arc::{Arc, ArcBorrow, RawOffsetArc}; @@ -92,7 +93,8 @@ use style::gecko_bindings::structs; use style::gecko_bindings::structs::{CallerType, CSSPseudoElementType, CompositeOperation}; use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets}; use style::gecko_bindings::structs::{RawServoStyleRule, ComputedStyleStrong, RustString}; -use style::gecko_bindings::structs::{ServoStyleSheet, SheetLoadData, SheetParsingMode, nsAtom, nsCSSPropertyID}; +use style::gecko_bindings::structs::{ServoStyleSheet, SheetLoadData, SheetLoadDataHolder}; +use style::gecko_bindings::structs::{SheetParsingMode, nsAtom, nsCSSPropertyID}; use style::gecko_bindings::structs::{nsCSSFontDesc, nsCSSCounterDesc}; use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair}; use style::gecko_bindings::structs::AtomArray; @@ -145,6 +147,7 @@ use style::stylesheets::{DocumentRule, FontFaceRule, FontFeatureValuesRule, Impo use style::stylesheets::{KeyframesRule, MediaRule, NamespaceRule, Origin, OriginSet, PageRule}; use style::stylesheets::{StyleRule, StylesheetContents, SupportsRule}; use style::stylesheets::StylesheetLoader as StyleStylesheetLoader; +use style::stylesheets::import_rule::ImportSheet; use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue}; use style::stylesheets::supports_rule::parse_condition_or_declaration; use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist}; @@ -163,7 +166,7 @@ use style::values::specified::gecko::{IntersectionObserverRootMargin, PixelOrPer use style::values::specified::source_size_list::SourceSizeList; use style_traits::{CssWriter, ParsingMode, StyleParseErrorKind, ToCss}; use super::error_reporter::ErrorReporter; -use super::stylesheet_loader::StylesheetLoader; +use super::stylesheet_loader::{AsyncStylesheetParser, StylesheetLoader}; /* * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in @@ -1122,6 +1125,15 @@ pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyl ).into_strong() } +fn mode_to_origin(mode: SheetParsingMode) -> Origin { + match mode { + SheetParsingMode::eAuthorSheetFeatures => Origin::Author, + SheetParsingMode::eUserSheetFeatures => Origin::User, + SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent, + SheetParsingMode::eSafeAgentSheetFeatures => Origin::UserAgent, + } +} + /// Note: The load_data corresponds to this sheet, and is passed as the parent /// load data for child sheet loads. It may be null for certain cases where we /// know we won't have child loads. @@ -1140,13 +1152,6 @@ pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes( let global_style_data = &*GLOBAL_STYLE_DATA; let input: &str = unsafe { (*bytes).as_str_unchecked() }; - let origin = match mode { - SheetParsingMode::eAuthorSheetFeatures => Origin::Author, - SheetParsingMode::eUserSheetFeatures => Origin::User, - SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent, - SheetParsingMode::eSafeAgentSheetFeatures => Origin::UserAgent, - }; - let reporter = ErrorReporter::new(stylesheet, loader, extra_data); let url_data = unsafe { RefPtr::from_ptr_ref(&extra_data) }; let loader = if loader.is_null() { @@ -1163,12 +1168,44 @@ pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes( Arc::new(StylesheetContents::from_str( - input, url_data.clone(), origin, + input, url_data.clone(), mode_to_origin(mode), &global_style_data.shared_lock, loader, &reporter, quirks_mode.into(), line_number_offset) ).into_strong() } +#[no_mangle] +pub extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync( + load_data: *mut SheetLoadDataHolder, + extra_data: *mut URLExtraData, + bytes: *const nsACString, + mode: SheetParsingMode, + line_number_offset: u32, + quirks_mode: nsCompatibility, +) { + let (load_data, extra_data, bytes) = unsafe { + let mut b = nsCString::new(); + b.assign(&*bytes); + (RefPtr::new(load_data), RefPtr::new(extra_data), b) + }; + let async_parser = AsyncStylesheetParser::new( + load_data, + extra_data, + bytes, + mode_to_origin(mode), + quirks_mode.into(), + line_number_offset + ); + + if let Some(thread_pool) = STYLE_THREAD_POOL.style_thread_pool.as_ref() { + thread_pool.spawn(|| { + async_parser.parse(); + }); + } else { + async_parser.parse(); + } +} + #[no_mangle] pub extern "C" fn Servo_StyleSet_AppendStyleSheet( raw_data: RawServoStyleSetBorrowed, @@ -2051,6 +2088,17 @@ pub extern "C" fn Servo_ImportRule_GetSheet( }) } +#[no_mangle] +pub extern "C" fn Servo_ImportRule_SetSheet( + rule: RawServoImportRuleBorrowed, + sheet: *mut ServoStyleSheet, +) { + write_locked_arc(rule, |rule: &mut ImportRule| { + let sheet = unsafe { GeckoStyleSheet::new(sheet) }; + rule.stylesheet = ImportSheet::new(sheet); + }) +} + #[no_mangle] pub extern "C" fn Servo_Keyframe_GetKeyText( keyframe: RawServoKeyframeBorrowed, diff --git a/ports/geckolib/lib.rs b/ports/geckolib/lib.rs index 80c22e54707..d48a096df64 100644 --- a/ports/geckolib/lib.rs +++ b/ports/geckolib/lib.rs @@ -9,6 +9,7 @@ extern crate env_logger; extern crate libc; #[macro_use] extern crate log; extern crate malloc_size_of; +extern crate nsstring; extern crate selectors; extern crate servo_arc; extern crate smallvec; diff --git a/ports/geckolib/stylesheet_loader.rs b/ports/geckolib/stylesheet_loader.rs index 1dfe30e8bdb..0c320fee897 100644 --- a/ports/geckolib/stylesheet_loader.rs +++ b/ports/geckolib/stylesheet_loader.rs @@ -3,15 +3,24 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use cssparser::SourceLocation; +use nsstring::nsCString; use servo_arc::Arc; +use style::context::QuirksMode; +use style::error_reporting::NullReporter; use style::gecko::data::GeckoStyleSheet; +use style::gecko::global_style_data::GLOBAL_STYLE_DATA; +use style::gecko_bindings::bindings; use style::gecko_bindings::bindings::Gecko_LoadStyleSheet; -use style::gecko_bindings::structs::{Loader, ServoStyleSheet, SheetLoadData, LoaderReusableStyleSheets}; +use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets}; +use style::gecko_bindings::structs::{ServoStyleSheet, SheetLoadData, SheetLoadDataHolder}; +use style::gecko_bindings::structs::URLExtraData; use style::gecko_bindings::sugar::ownership::FFIArcHelpers; +use style::gecko_bindings::sugar::refptr::RefPtr; use style::media_queries::MediaList; use style::parser::ParserContext; use style::shared_lock::{Locked, SharedRwLock}; -use style::stylesheets::{ImportRule, StylesheetLoader as StyleStylesheetLoader}; +use style::stylesheets::{ImportRule, Origin, StylesheetLoader as StyleStylesheetLoader}; +use style::stylesheets::StylesheetContents; use style::stylesheets::import_rule::ImportSheet; use style::values::CssUrl; @@ -41,15 +50,11 @@ impl StyleStylesheetLoader for StylesheetLoader { // so this raw pointer will still be valid. let child_sheet = unsafe { - let (spec_bytes, spec_len) = url.as_slice_components(); - let base_url_data = url.extra_data.get(); Gecko_LoadStyleSheet(self.0, self.1, self.2, self.3, - base_url_data, - spec_bytes, - spec_len as u32, + url.for_ffi(), media.into_strong()) }; @@ -60,3 +65,75 @@ impl StyleStylesheetLoader for StylesheetLoader { Arc::new(lock.wrap(ImportRule { url, source_location, stylesheet })) } } + +pub struct AsyncStylesheetParser { + load_data: RefPtr, + extra_data: RefPtr, + bytes: nsCString, + origin: Origin, + quirks_mode: QuirksMode, + line_number_offset: u32, +} + +impl AsyncStylesheetParser { + pub fn new( + load_data: RefPtr, + extra_data: RefPtr, + bytes: nsCString, + origin: Origin, + quirks_mode: QuirksMode, + line_number_offset: u32, + ) -> Self { + AsyncStylesheetParser { + load_data, + extra_data, + bytes, + origin, + quirks_mode, + line_number_offset, + } + } + + pub fn parse(self) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let input: &str = unsafe { (*self.bytes).as_str_unchecked() }; + + // Note: Parallel CSS parsing doesn't report CSS errors. When errors + // are being logged, Gecko prevents the parallel parsing path from + // running. + let sheet = Arc::new(StylesheetContents::from_str( + input, self.extra_data.clone(), self.origin, + &global_style_data.shared_lock, Some(&self), &NullReporter, + self.quirks_mode.into(), self.line_number_offset) + ); + + unsafe { + bindings::Gecko_StyleSheet_FinishAsyncParse(self.load_data.get(), sheet.into_strong()); + } + } +} + +impl StyleStylesheetLoader for AsyncStylesheetParser { + fn request_stylesheet( + &self, + url: CssUrl, + source_location: SourceLocation, + _context: &ParserContext, + lock: &SharedRwLock, + media: Arc>, + ) -> Arc> { + let stylesheet = ImportSheet::new_pending(self.origin, self.quirks_mode); + let rule = Arc::new(lock.wrap(ImportRule { url: url.clone(), source_location, stylesheet })); + + unsafe { + bindings::Gecko_LoadStyleSheetAsync( + self.load_data.get(), + url.for_ffi(), + media.into_strong(), + rule.clone().into_strong() + ); + } + + rule + } +}