servo: Merge #13553 - Support creating StyleAnimationValue objects from Servo (from birtles:animvalues); r=Manishearth
authorBrian Birtles <birtles@gmail.com>
Sun, 09 Oct 2016 08:53:28 -0500
changeset 339864 7163744bebb572cfe235b79cab64561f7b0bbeb7
parent 339863 f0e206df7035fb12d6a7e42b053b48be30117998
child 339865 55efee3a60a78bd022e520243196facc811cf683
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersManishearth
bugs1302949
servo: Merge #13553 - Support creating StyleAnimationValue objects from Servo (from birtles:animvalues); r=Manishearth These are the servo-side changes for [bug 1302949](https://bugzilla.mozilla.org/show_bug.cgi?id=1302949#c59). @Manishearth has already reviewed them there. --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix [bug 1302949](https://bugzilla.mozilla.org/show_bug.cgi?id=1302949#c59) - [X] These changes do not require tests because there are existing tests for this in mozilla-central Source-Repo: https://github.com/servo/servo Source-Revision: 804317c8858a096eb99e1ff0baf860dbdfcbb04d
servo/components/style/gecko/wrapper.rs
servo/components/style/gecko_bindings/bindings.rs
servo/ports/geckolib/Cargo.lock
servo/ports/geckolib/Cargo.toml
servo/ports/geckolib/glue.rs
servo/ports/geckolib/lib.rs
servo/tests/unit/stylo/Cargo.toml
servo/tests/unit/stylo/lib.rs
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -65,16 +65,26 @@ pub struct GeckoDeclarationBlock {
     // instance. It wouldn't provide safety as Rust usually promises,
     // but it is fine as far as we only access them in a single thread.
     // If we need to access them in different threads, we would need
     // to redesign how it works with MiscContainer in Gecko side.
     pub cache: AtomicPtr<bindings::nsHTMLCSSStyleSheet>,
     pub immutable: AtomicBool,
 }
 
+impl PartialEq for GeckoDeclarationBlock {
+    fn eq(&self, other: &GeckoDeclarationBlock) -> bool {
+        match (&self.declarations, &other.declarations) {
+            (&None, &None) => true,
+            (&Some(ref s), &Some(ref other)) => *s.read() == *other.read(),
+            _ => false,
+        }
+    }
+}
+
 unsafe impl HasFFI for GeckoDeclarationBlock {
     type FFIType = bindings::ServoDeclarationBlock;
 }
 unsafe impl HasArcFFI for GeckoDeclarationBlock {}
 
 
 // We can eliminate OpaqueStyleData when the bindings move into the style crate.
 fn to_opaque_style_data(d: *mut NonOpaqueStyleData) -> *mut OpaqueStyleData {
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -160,16 +160,17 @@ unsafe impl Sync for nsStyleContext {}
 use gecko_bindings::structs::StyleClipPath;
 use gecko_bindings::structs::StyleBasicShapeType;
 use gecko_bindings::structs::StyleBasicShape;
 use gecko_bindings::structs::nsCSSShadowArray;
 use gecko_bindings::structs::nsINode;
 use gecko_bindings::structs::nsIDocument;
 use gecko_bindings::structs::nsIPrincipal;
 use gecko_bindings::structs::nsIURI;
+use gecko_bindings::structs::nsString;
 use gecko_bindings::structs::RawGeckoNode;
 use gecko_bindings::structs::RawGeckoElement;
 use gecko_bindings::structs::RawGeckoDocument;
 use gecko_bindings::structs::ServoNodeData;
 
 extern "C" {
     pub fn Gecko_EnsureTArrayCapacity(aArray: *mut ::std::os::raw::c_void,
                                       aCapacity: usize, aElementSize: usize);
@@ -395,16 +396,21 @@ extern "C" {
 }
 extern "C" {
     pub fn Gecko_AtomEqualsUTF8IgnoreCase(aAtom: *mut nsIAtom,
                                           aString:
                                               *const ::std::os::raw::c_char,
                                           aLength: u32) -> bool;
 }
 extern "C" {
+    pub fn Gecko_Utf8SliceToString(aString: *mut nsString,
+                                   aBuffer: *const u8,
+                                   aBufferLen: usize);
+}
+extern "C" {
     pub fn Gecko_FontFamilyList_Clear(aList: *mut FontFamilyList);
 }
 extern "C" {
     pub fn Gecko_FontFamilyList_AppendNamed(aList: *mut FontFamilyList,
                                             aName: *mut nsIAtom);
 }
 extern "C" {
     pub fn Gecko_FontFamilyList_AppendGeneric(list: *mut FontFamilyList,
@@ -843,29 +849,46 @@ extern "C" {
     pub fn Servo_StyleSet_InsertStyleSheetBefore(set:
                                                      RawServoStyleSetBorrowedMut,
                                                  sheet:
                                                      RawServoStyleSheetBorrowed,
                                                  reference:
                                                      RawServoStyleSheetBorrowed);
 }
 extern "C" {
+    pub fn Servo_ParseProperty(property_bytes: *const u8,
+                               property_length: u32,
+                               value_bytes: *const u8,
+                               value_length: u32,
+                               base_bytes: *const u8,
+                               base_length: u32,
+                               base: *mut ThreadSafeURIHolder,
+                               referrer: *mut ThreadSafeURIHolder,
+                               principal: *mut ThreadSafePrincipalHolder)
+    -> ServoDeclarationBlockStrong;
+}
+extern "C" {
     pub fn Servo_ParseStyleAttribute(bytes: *const u8, length: u32,
                                      cache: *mut nsHTMLCSSStyleSheet)
      -> ServoDeclarationBlockStrong;
 }
 extern "C" {
     pub fn Servo_DeclarationBlock_AddRef(declarations:
                                              ServoDeclarationBlockBorrowed);
 }
 extern "C" {
     pub fn Servo_DeclarationBlock_Release(declarations:
                                               ServoDeclarationBlockBorrowed);
 }
 extern "C" {
+    pub fn Servo_DeclarationBlock_Equals(a: ServoDeclarationBlockBorrowed,
+                                         b: ServoDeclarationBlockBorrowed)
+     -> bool;
+}
+extern "C" {
     pub fn Servo_DeclarationBlock_GetCache(declarations:
                                                ServoDeclarationBlockBorrowed)
      -> *mut nsHTMLCSSStyleSheet;
 }
 extern "C" {
     pub fn Servo_DeclarationBlock_SetImmutable(declarations:
                                                    ServoDeclarationBlockBorrowed);
 }
@@ -925,16 +948,21 @@ extern "C" {
                                     set: RawServoStyleSetBorrowed)
      -> nsRestyleHint;
 }
 extern "C" {
     pub fn Servo_RestyleSubtree(node: RawGeckoNodeBorrowed,
                                 set: RawServoStyleSetBorrowedMut);
 }
 extern "C" {
+    pub fn Servo_RestyleWithAddedDeclaration(declarations: ServoDeclarationBlockBorrowed,
+                                             previous_style: ServoComputedValuesBorrowed)
+     -> ServoComputedValuesStrong;
+}
+extern "C" {
     pub fn Servo_GetStyleFont(computed_values:
                                   ServoComputedValuesBorrowedOrNull)
      -> *const nsStyleFont;
 }
 extern "C" {
     pub fn Servo_GetStyleColor(computed_values:
                                    ServoComputedValuesBorrowedOrNull)
      -> *const nsStyleColor;
--- a/servo/ports/geckolib/Cargo.lock
+++ b/servo/ports/geckolib/Cargo.lock
@@ -1,13 +1,14 @@
 [root]
 name = "geckoservo"
 version = "0.0.1"
 dependencies = [
  "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -402,16 +403,17 @@ dependencies = [
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "stylo_tests"
 version = "0.0.1"
 dependencies = [
  "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "geckoservo 0.0.1",
  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/servo/ports/geckolib/Cargo.toml
+++ b/servo/ports/geckolib/Cargo.toml
@@ -6,16 +6,17 @@ license = "MPL-2.0"
 
 [lib]
 name = "geckoservo"
 path = "lib.rs"
 crate-type = ["staticlib", "rlib"]
 
 [dependencies]
 app_units = "0.3"
+cssparser = {version = "0.7"}
 env_logger = "0.3"
 euclid = "0.10.1"
 lazy_static = "0.2"
 libc = "0.2"
 log = {version = "0.3.5", features = ["release_max_level_info"]}
 num_cpus = "0.2.2"
 parking_lot = "0.3"
 selectors = "0.13"
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1,13 +1,14 @@
 /* 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/. */
 
 use app_units::Au;
+use cssparser::{Parser, ToCss};
 use env_logger;
 use euclid::Size2D;
 use parking_lot::RwLock;
 use std::mem::transmute;
 use std::ptr;
 use std::slice;
 use std::str::from_utf8_unchecked;
 use std::sync::{Arc, Mutex};
@@ -24,27 +25,32 @@ use style::gecko::wrapper::{DUMMY_BASE_U
 use style::gecko::wrapper::{GeckoElement, GeckoNode};
 use style::gecko_bindings::bindings::{RawGeckoElementBorrowed, RawGeckoNodeBorrowed};
 use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned};
 use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed};
 use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong};
 use style::gecko_bindings::bindings::{ServoDeclarationBlockBorrowed, ServoDeclarationBlockStrong};
 use style::gecko_bindings::bindings::{ThreadSafePrincipalHolder, ThreadSafeURIHolder};
 use style::gecko_bindings::bindings::{nsHTMLCSSStyleSheet, ServoComputedValuesBorrowedOrNull};
+use style::gecko_bindings::bindings::Gecko_Utf8SliceToString;
 use style::gecko_bindings::bindings::RawServoStyleSetBorrowedMut;
 use style::gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
 use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom};
 use style::gecko_bindings::structs::ServoElementSnapshot;
 use style::gecko_bindings::structs::nsRestyleHint;
+use style::gecko_bindings::structs::nsString;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasBoxFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::parallel;
-use style::parser::ParserContextExtraData;
-use style::properties::{ComputedValues, parse_one_declaration};
+use style::parser::{ParserContext, ParserContextExtraData};
+use style::properties::{ComputedValues, Importance, PropertyDeclaration};
+use style::properties::{PropertyDeclarationParseResult, PropertyDeclarationBlock};
+use style::properties::{cascade, parse_one_declaration};
 use style::selector_impl::PseudoElementCascadeType;
+use style::selector_matching::ApplicableDeclarationBlock;
 use style::sequential;
 use style::string_cache::Atom;
 use style::stylesheets::{Origin, Stylesheet};
 use style::timer::Timer;
 use url::Url;
 
 /*
  * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
@@ -86,16 +92,17 @@ fn restyle_subtree(node: GeckoNode, raw_
     // The stylist consumes stylesheets lazily.
     let per_doc_data = PerDocumentStyleData::from_ffi_mut(raw_data);
     per_doc_data.flush_stylesheets();
 
     let local_context_data =
         LocalStyleContextCreationInfo::new(per_doc_data.new_animations_sender.clone());
 
     let shared_style_context = SharedStyleContext {
+        // FIXME (bug 1303229): Use the actual viewport size here
         viewport_size: Size2D::new(Au(0), Au(0)),
         screen_size_changed: false,
         generation: 0,
         goal: ReflowGoal::ForScriptQuery,
         stylist: per_doc_data.stylist.clone(),
         running_animations: per_doc_data.running_animations.clone(),
         expired_animations: per_doc_data.expired_animations.clone(),
         error_reporter: Box::new(StdoutErrorReporter),
@@ -116,16 +123,45 @@ fn restyle_subtree(node: GeckoNode, raw_
 #[no_mangle]
 pub extern "C" fn Servo_RestyleSubtree(node: RawGeckoNodeBorrowed,
                                        raw_data: RawServoStyleSetBorrowedMut) -> () {
     let node = GeckoNode(node);
     restyle_subtree(node, raw_data);
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_RestyleWithAddedDeclaration(declarations: ServoDeclarationBlockBorrowed,
+                                                    previous_style: ServoComputedValuesBorrowed)
+  -> ServoComputedValuesStrong
+{
+    match GeckoDeclarationBlock::as_arc(&declarations).declarations {
+        Some(ref declarations) => {
+            let declaration_block = ApplicableDeclarationBlock {
+                mixed_declarations: declarations.clone(),
+                importance: Importance::Normal,
+                source_order: 0,
+                specificity: ::std::u32::MAX,
+            };
+            let previous_style = ComputedValues::as_arc(&previous_style);
+
+            // FIXME (bug 1303229): Use the actual viewport size here
+            let (computed, _) = cascade(Size2D::new(Au(0), Au(0)),
+                                        &[declaration_block],
+                                        false,
+                                        Some(previous_style),
+                                        None,
+                                        None,
+                                        Box::new(StdoutErrorReporter));
+            Arc::new(computed).into_strong()
+        },
+        None => ServoComputedValuesStrong::null(),
+    }
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_StyleWorkerThreadCount() -> u32 {
     *NUM_THREADS as u32
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_Node_ClearNodeData(node: RawGeckoNodeBorrowed) -> () {
     let node = GeckoNode(node);
     node.clear_data();
@@ -333,16 +369,64 @@ pub extern "C" fn Servo_StyleSet_Init() 
     data.into_ffi()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_Drop(data: RawServoStyleSetOwned) -> () {
     let _ = data.into_box::<PerDocumentStyleData>();
 }
 
+
+#[no_mangle]
+pub extern "C" fn Servo_ParseProperty(property_bytes: *const u8,
+                                      property_length: u32,
+                                      value_bytes: *const u8,
+                                      value_length: u32,
+                                      base_bytes: *const u8,
+                                      base_length: u32,
+                                      base: *mut ThreadSafeURIHolder,
+                                      referrer: *mut ThreadSafeURIHolder,
+                                      principal: *mut ThreadSafePrincipalHolder)
+                                      -> ServoDeclarationBlockStrong {
+    // All this string wrangling is temporary until the Gecko string bindings land (bug 1294742).
+    let name = unsafe { from_utf8_unchecked(slice::from_raw_parts(property_bytes,
+                                                                  property_length as usize)) };
+    let value_str = unsafe { from_utf8_unchecked(slice::from_raw_parts(value_bytes,
+                                                                       value_length as usize)) };
+    let base_str = unsafe { from_utf8_unchecked(slice::from_raw_parts(base_bytes,
+                                                                      base_length as usize)) };
+    let base_url = Url::parse(base_str).unwrap();
+    let extra_data = ParserContextExtraData {
+        base: Some(GeckoArcURI::new(base)),
+        referrer: Some(GeckoArcURI::new(referrer)),
+        principal: Some(GeckoArcPrincipal::new(principal)),
+    };
+
+    let context = ParserContext::new_with_extra_data(Origin::Author, &base_url,
+                                                     Box::new(StdoutErrorReporter),
+                                                     extra_data);
+
+    let mut results = vec![];
+    match PropertyDeclaration::parse(name, &context, &mut Parser::new(value_str),
+                                     &mut results, false) {
+        PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {},
+        _ => return ServoDeclarationBlockStrong::null(),
+    }
+
+    let results = results.into_iter().map(|r| (r, Importance::Normal)).collect();
+
+    Arc::new(GeckoDeclarationBlock {
+        declarations: Some(Arc::new(RwLock::new(PropertyDeclarationBlock {
+            declarations: results,
+            important_count: 0,
+        }))),
+        cache: AtomicPtr::new(ptr::null_mut()),
+        immutable: AtomicBool::new(false),
+    }).into_strong()
+}
 #[no_mangle]
 pub extern "C" fn Servo_ParseStyleAttribute(bytes: *const u8, length: u32,
                                             cache: *mut nsHTMLCSSStyleSheet)
                                             -> ServoDeclarationBlockStrong {
     let value = unsafe { from_utf8_unchecked(slice::from_raw_parts(bytes, length as usize)) };
     Arc::new(GeckoDeclarationBlock {
         declarations: GeckoElement::parse_style_attribute(value).map(|block| {
             Arc::new(RwLock::new(block))
@@ -358,32 +442,72 @@ pub extern "C" fn Servo_DeclarationBlock
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_Release(declarations: ServoDeclarationBlockBorrowed) {
     unsafe { GeckoDeclarationBlock::release(declarations) };
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_Equals(a: ServoDeclarationBlockBorrowed,
+                                                b: ServoDeclarationBlockBorrowed)
+                                                -> bool {
+    GeckoDeclarationBlock::as_arc(&a) == GeckoDeclarationBlock::as_arc(&b)
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_GetCache(declarations: ServoDeclarationBlockBorrowed)
                                                  -> *mut nsHTMLCSSStyleSheet {
     GeckoDeclarationBlock::as_arc(&declarations).cache.load(Ordering::Relaxed)
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_SetImmutable(declarations: ServoDeclarationBlockBorrowed) {
     GeckoDeclarationBlock::as_arc(&declarations).immutable.store(true, Ordering::Relaxed)
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_ClearCachePointer(declarations: ServoDeclarationBlockBorrowed) {
     GeckoDeclarationBlock::as_arc(&declarations).cache.store(ptr::null_mut(), Ordering::Relaxed)
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SerializeOneValue(
+    declarations: ServoDeclarationBlockBorrowed,
+    buffer: *mut nsString)
+{
+    let mut string = String::new();
+
+    if let Some(ref declarations) = GeckoDeclarationBlock::as_arc(&declarations).declarations {
+        declarations.read().to_css(&mut string).unwrap();
+        // FIXME: We are expecting |declarations| to be a declaration block with either a single
+        // longhand property-declaration or a series of longhand property-declarations that make
+        // up a single shorthand property. As a result, it should be possible to serialize
+        // |declarations| as a single declaration. However, we only want to return the *value* from
+        // that single declaration. For now, we just manually strip the property name, colon,
+        // leading spacing, and trailing space. In future we should find a more robust way to do
+        // this.
+        //
+        // See https://github.com/servo/servo/issues/13423
+        debug_assert!(string.find(':').is_some());
+        let position = string.find(':').unwrap();
+        // Get the value after the first colon and any following whitespace.
+        let value = &string[(position + 1)..].trim_left();
+        debug_assert!(value.ends_with(';'));
+        let length = value.len() - 1; // Strip last semicolon.
+
+        // FIXME: Once we have nsString bindings for Servo (bug 1294742), we should be able to drop
+        // this and fill in |buffer| directly.
+        unsafe {
+            Gecko_Utf8SliceToString(buffer, value.as_ptr(), length);
+        }
+    }
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_CSSSupports(property: *const u8, property_length: u32,
                                     value: *const u8, value_length: u32) -> bool {
     let property = unsafe { from_utf8_unchecked(slice::from_raw_parts(property, property_length as usize)) };
     let value    = unsafe { from_utf8_unchecked(slice::from_raw_parts(value, value_length as usize)) };
 
     let base_url = &*DUMMY_BASE_URL;
     let extra_data = ParserContextExtraData::default();
 
--- a/servo/ports/geckolib/lib.rs
+++ b/servo/ports/geckolib/lib.rs
@@ -1,15 +1,16 @@
 /* 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/. */
 
 
 #[macro_use]extern crate style;
 extern crate app_units;
+extern crate cssparser;
 extern crate env_logger;
 extern crate euclid;
 extern crate libc;
 #[macro_use] extern crate log;
 extern crate parking_lot;
 extern crate url;
 
 #[allow(non_snake_case)]
--- a/servo/tests/unit/stylo/Cargo.toml
+++ b/servo/tests/unit/stylo/Cargo.toml
@@ -8,16 +8,17 @@ build = "build.rs"
 
 [lib]
 name = "stylo_tests"
 path = "lib.rs"
 doctest = false
 
 [dependencies]
 app_units = "0.3"
+cssparser = {version = "0.7"}
 env_logger = "0.3"
 euclid = "0.10.1"
 lazy_static = "0.2"
 libc = "0.2"
 log = {version = "0.3.5", features = ["release_max_level_info"]}
 num_cpus = "0.2.2"
 parking_lot = "0.3"
 selectors = "0.13"
--- a/servo/tests/unit/stylo/lib.rs
+++ b/servo/tests/unit/stylo/lib.rs
@@ -1,13 +1,14 @@
 /* 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/. */
 
 extern crate app_units;
+extern crate cssparser;
 extern crate env_logger;
 extern crate euclid;
 extern crate geckoservo;
 extern crate libc;
 #[macro_use] extern crate log;
 extern crate parking_lot;
 extern crate style;
 extern crate url;