1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 //! This module contains conversion helpers between Servo and Gecko types
6 //! Ideally, it would be in geckolib itself, but coherence
7 //! forces us to keep the traits and implementations here
8
9 #![allow(unsafe_code)]
10
11 use app_units::Au;
12 use gecko::values::{convert_rgba_to_nscolor, GeckoStyleCoordConvertible};
13 use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetLayerImageImageValue};
14 use gecko_bindings::bindings::{Gecko_InitializeImageCropRect, Gecko_SetImageElement};
15 use gecko_bindings::structs::{self, nsCSSUnit, nsStyleCoord_CalcValue};
16 use gecko_bindings::structs::{nsStyleImage, nsresult, SheetType};
17 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
18 use std::f32::consts::PI;
19 use stylesheets::{Origin, RulesMutateError};
20 use values::computed::{Angle, CalcLengthOrPercentage, ComputedImageUrl, Gradient, Image};
21 use values::computed::{Integer, LengthOrPercentage, LengthOrPercentageOrAuto, Percentage, TextAlign};
22 use values::generics::box_::VerticalAlign;
23 use values::generics::grid::{TrackListValue, TrackSize};
24 use values::generics::image::{CompatMode, Image as GenericImage, GradientItem};
25 use values::generics::rect::Rect;
26
27 impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue28 fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
29 let has_percentage = other.percentage.is_some();
30 nsStyleCoord_CalcValue {
31 mLength: other.unclamped_length().to_i32_au(),
32 mPercent: other.percentage.map_or(0., |p| p.0),
33 mHasPercent: has_percentage,
34 }
35 }
36 }
37
38 impl From<nsStyleCoord_CalcValue> for CalcLengthOrPercentage {
from(other: nsStyleCoord_CalcValue) -> CalcLengthOrPercentage39 fn from(other: nsStyleCoord_CalcValue) -> CalcLengthOrPercentage {
40 let percentage = if other.mHasPercent {
41 Some(Percentage(other.mPercent))
42 } else {
43 None
44 };
45 Self::new(Au(other.mLength).into(), percentage)
46 }
47 }
48
49 impl From<LengthOrPercentage> for nsStyleCoord_CalcValue {
from(other: LengthOrPercentage) -> nsStyleCoord_CalcValue50 fn from(other: LengthOrPercentage) -> nsStyleCoord_CalcValue {
51 match other {
52 LengthOrPercentage::Length(px) => {
53 nsStyleCoord_CalcValue {
54 mLength: px.to_i32_au(),
55 mPercent: 0.0,
56 mHasPercent: false,
57 }
58 },
59 LengthOrPercentage::Percentage(pc) => {
60 nsStyleCoord_CalcValue {
61 mLength: 0,
62 mPercent: pc.0,
63 mHasPercent: true,
64 }
65 },
66 LengthOrPercentage::Calc(calc) => calc.into(),
67 }
68 }
69 }
70
71 impl LengthOrPercentageOrAuto {
72 /// Convert this value in an appropriate `nsStyleCoord::CalcValue`.
to_calc_value(&self) -> Option<nsStyleCoord_CalcValue>73 pub fn to_calc_value(&self) -> Option<nsStyleCoord_CalcValue> {
74 match *self {
75 LengthOrPercentageOrAuto::Length(px) => {
76 Some(nsStyleCoord_CalcValue {
77 mLength: px.to_i32_au(),
78 mPercent: 0.0,
79 mHasPercent: false,
80 })
81 },
82 LengthOrPercentageOrAuto::Percentage(pc) => {
83 Some(nsStyleCoord_CalcValue {
84 mLength: 0,
85 mPercent: pc.0,
86 mHasPercent: true,
87 })
88 },
89 LengthOrPercentageOrAuto::Calc(calc) => Some(calc.into()),
90 LengthOrPercentageOrAuto::Auto => None,
91 }
92 }
93 }
94
95 impl From<nsStyleCoord_CalcValue> for LengthOrPercentage {
from(other: nsStyleCoord_CalcValue) -> LengthOrPercentage96 fn from(other: nsStyleCoord_CalcValue) -> LengthOrPercentage {
97 match (other.mHasPercent, other.mLength) {
98 (false, _) => LengthOrPercentage::Length(Au(other.mLength).into()),
99 (true, 0) => LengthOrPercentage::Percentage(Percentage(other.mPercent)),
100 _ => LengthOrPercentage::Calc(other.into()),
101 }
102 }
103 }
104
105 impl From<nsStyleCoord_CalcValue> for LengthOrPercentageOrAuto {
from(other: nsStyleCoord_CalcValue) -> LengthOrPercentageOrAuto106 fn from(other: nsStyleCoord_CalcValue) -> LengthOrPercentageOrAuto {
107 match (other.mHasPercent, other.mLength) {
108 (false, _) => LengthOrPercentageOrAuto::Length(Au(other.mLength).into()),
109 (true, 0) => LengthOrPercentageOrAuto::Percentage(Percentage(other.mPercent)),
110 _ => LengthOrPercentageOrAuto::Calc(other.into()),
111 }
112 }
113 }
114
115 impl From<Angle> for CoordDataValue {
from(reference: Angle) -> Self116 fn from(reference: Angle) -> Self {
117 match reference {
118 Angle::Deg(val) => CoordDataValue::Degree(val),
119 Angle::Grad(val) => CoordDataValue::Grad(val),
120 Angle::Rad(val) => CoordDataValue::Radian(val),
121 Angle::Turn(val) => CoordDataValue::Turn(val),
122 }
123 }
124 }
125
126 impl Angle {
127 /// Converts Angle struct into (value, unit) pair.
to_gecko_values(&self) -> (f32, nsCSSUnit)128 pub fn to_gecko_values(&self) -> (f32, nsCSSUnit) {
129 match *self {
130 Angle::Deg(val) => (val, nsCSSUnit::eCSSUnit_Degree),
131 Angle::Grad(val) => (val, nsCSSUnit::eCSSUnit_Grad),
132 Angle::Rad(val) => (val, nsCSSUnit::eCSSUnit_Radian),
133 Angle::Turn(val) => (val, nsCSSUnit::eCSSUnit_Turn),
134 }
135 }
136
137 /// Converts gecko (value, unit) pair into Angle struct
from_gecko_values(value: f32, unit: nsCSSUnit) -> Angle138 pub fn from_gecko_values(value: f32, unit: nsCSSUnit) -> Angle {
139 match unit {
140 nsCSSUnit::eCSSUnit_Degree => Angle::Deg(value),
141 nsCSSUnit::eCSSUnit_Grad => Angle::Grad(value),
142 nsCSSUnit::eCSSUnit_Radian => Angle::Rad(value),
143 nsCSSUnit::eCSSUnit_Turn => Angle::Turn(value),
144 _ => panic!("Unexpected unit for angle"),
145 }
146 }
147 }
148
149 impl nsStyleImage {
150 /// Set a given Servo `Image` value into this `nsStyleImage`.
set(&mut self, image: Image)151 pub fn set(&mut self, image: Image) {
152 match image {
153 GenericImage::Gradient(boxed_gradient) => {
154 self.set_gradient(*boxed_gradient)
155 },
156 GenericImage::Url(ref url) => {
157 unsafe {
158 Gecko_SetLayerImageImageValue(self, url.image_value.get());
159 }
160 },
161 GenericImage::Rect(ref image_rect) => {
162 unsafe {
163 Gecko_SetLayerImageImageValue(self, image_rect.url.image_value.get());
164 Gecko_InitializeImageCropRect(self);
165
166 // Set CropRect
167 let ref mut rect = *self.mCropRect.mPtr;
168 image_rect.top.to_gecko_style_coord(&mut rect.data_at_mut(0));
169 image_rect.right.to_gecko_style_coord(&mut rect.data_at_mut(1));
170 image_rect.bottom.to_gecko_style_coord(&mut rect.data_at_mut(2));
171 image_rect.left.to_gecko_style_coord(&mut rect.data_at_mut(3));
172 }
173 }
174 GenericImage::Element(ref element) => {
175 unsafe {
176 Gecko_SetImageElement(self, element.as_ptr());
177 }
178 }
179 }
180 }
181
set_gradient(&mut self, gradient: Gradient)182 fn set_gradient(&mut self, gradient: Gradient) {
183 use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_CIRCULAR, NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL};
184 use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_LINEAR, NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER};
185 use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE};
186 use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE};
187 use gecko_bindings::structs::nsStyleCoord;
188 use values::computed::image::LineDirection;
189 use values::generics::image::{Circle, Ellipse, EndingShape, GradientKind, ShapeExtent};
190 use values::specified::position::{X, Y};
191
192 let stop_count = gradient.items.len();
193 if stop_count >= ::std::u32::MAX as usize {
194 warn!("stylo: Prevented overflow due to too many gradient stops");
195 return;
196 }
197
198 let gecko_gradient = match gradient.kind {
199 GradientKind::Linear(direction) => {
200 let gecko_gradient = unsafe {
201 Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8,
202 NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8,
203 gradient.repeating,
204 gradient.compat_mode != CompatMode::Modern,
205 gradient.compat_mode == CompatMode::Moz,
206 stop_count as u32)
207 };
208
209 match direction {
210 LineDirection::Angle(angle) => {
211 // PI radians (180deg) is ignored because it is the default value.
212 if angle.radians() != PI {
213 unsafe {
214 (*gecko_gradient).mAngle.set(angle);
215 }
216 }
217 },
218 LineDirection::Horizontal(x) => {
219 let x = match x {
220 X::Left => 0.0,
221 X::Right => 1.0,
222 };
223
224 unsafe {
225 (*gecko_gradient).mBgPosX
226 .set_value(CoordDataValue::Percent(x));
227 (*gecko_gradient).mBgPosY
228 .set_value(CoordDataValue::Percent(0.5));
229 }
230 },
231 LineDirection::Vertical(y) => {
232 // Although bottom is the default value, we can not ignore
233 // it here, because the rendering code of Gecko relies on
234 // this to behave correctly for legacy mode.
235 let y = match y {
236 Y::Top => 0.0,
237 Y::Bottom => 1.0,
238 };
239 unsafe {
240 (*gecko_gradient).mBgPosX
241 .set_value(CoordDataValue::Percent(0.5));
242 (*gecko_gradient).mBgPosY
243 .set_value(CoordDataValue::Percent(y));
244 }
245 },
246 LineDirection::Corner(horiz, vert) => {
247 let percent_x = match horiz {
248 X::Left => 0.0,
249 X::Right => 1.0,
250 };
251 let percent_y = match vert {
252 Y::Top => 0.0,
253 Y::Bottom => 1.0,
254 };
255
256 unsafe {
257 (*gecko_gradient).mBgPosX
258 .set_value(CoordDataValue::Percent(percent_x));
259 (*gecko_gradient).mBgPosY
260 .set_value(CoordDataValue::Percent(percent_y));
261 }
262 },
263 #[cfg(feature = "gecko")]
264 LineDirection::MozPosition(position, angle) => {
265 unsafe {
266 if let Some(position) = position {
267 (*gecko_gradient).mBgPosX.set(position.horizontal);
268 (*gecko_gradient).mBgPosY.set(position.vertical);
269 }
270 if let Some(angle) = angle {
271 (*gecko_gradient).mAngle.set(angle);
272 }
273 }
274 },
275 }
276 gecko_gradient
277 },
278 GradientKind::Radial(shape, position, angle) => {
279 let keyword_to_gecko_size = |keyword| {
280 match keyword {
281 ShapeExtent::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE,
282 ShapeExtent::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE,
283 ShapeExtent::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER,
284 ShapeExtent::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER,
285 ShapeExtent::Contain => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE,
286 ShapeExtent::Cover => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER,
287 }
288 };
289 let (gecko_shape, gecko_size) = match shape {
290 EndingShape::Circle(ref circle) => {
291 let size = match *circle {
292 Circle::Extent(extent) => {
293 keyword_to_gecko_size(extent)
294 },
295 _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE,
296 };
297 (NS_STYLE_GRADIENT_SHAPE_CIRCULAR as u8, size as u8)
298 },
299 EndingShape::Ellipse(ref ellipse) => {
300 let size = match *ellipse {
301 Ellipse::Extent(extent) => {
302 keyword_to_gecko_size(extent)
303 },
304 _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE,
305 };
306 (NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL as u8, size as u8)
307 }
308 };
309
310 let gecko_gradient = unsafe {
311 Gecko_CreateGradient(gecko_shape,
312 gecko_size,
313 gradient.repeating,
314 gradient.compat_mode == CompatMode::Moz,
315 gradient.compat_mode == CompatMode::Moz,
316 stop_count as u32)
317 };
318
319 // Clear mBgPos field and set mAngle if angle is set. Otherwise clear it.
320 unsafe {
321 if let Some(angle) = angle {
322 (*gecko_gradient).mAngle.set(angle);
323 }
324 }
325
326 // Setting radius values depending shape
327 match shape {
328 EndingShape::Circle(Circle::Radius(length)) => {
329 unsafe {
330 let au = length.to_i32_au();
331 (*gecko_gradient).mRadiusX.set_value(CoordDataValue::Coord(au));
332 (*gecko_gradient).mRadiusY.set_value(CoordDataValue::Coord(au));
333 }
334 },
335 EndingShape::Ellipse(Ellipse::Radii(x, y)) => {
336 unsafe {
337 (*gecko_gradient).mRadiusX.set(x);
338 (*gecko_gradient).mRadiusY.set(y);
339 }
340 },
341 _ => {},
342 }
343 unsafe {
344 (*gecko_gradient).mBgPosX.set(position.horizontal);
345 (*gecko_gradient).mBgPosY.set(position.vertical);
346 }
347
348 gecko_gradient
349 },
350 };
351
352 for (index, item) in gradient.items.iter().enumerate() {
353 // NB: stops are guaranteed to be none in the gecko side by
354 // default.
355
356 let gecko_stop = unsafe {
357 &mut (*gecko_gradient).mStops[index]
358 };
359 let mut coord = nsStyleCoord::null();
360
361 match *item {
362 GradientItem::ColorStop(ref stop) => {
363 gecko_stop.mColor = convert_rgba_to_nscolor(&stop.color);
364 gecko_stop.mIsInterpolationHint = false;
365 coord.set(stop.position);
366 },
367 GradientItem::InterpolationHint(hint) => {
368 gecko_stop.mIsInterpolationHint = true;
369 coord.set(Some(hint));
370 }
371 }
372
373 gecko_stop.mLocation.move_from(coord);
374 }
375
376 unsafe {
377 Gecko_SetGradientImageValue(self, gecko_gradient);
378 }
379 }
380
381 /// Converts into Image.
into_image(self: &nsStyleImage) -> Option<Image>382 pub unsafe fn into_image(self: &nsStyleImage) -> Option<Image> {
383 use gecko_bindings::bindings::Gecko_GetImageElement;
384 use gecko_bindings::structs::nsStyleImageType;
385 use values::computed::{NumberOrPercentage, MozImageRect};
386
387 match self.mType {
388 nsStyleImageType::eStyleImageType_Null => {
389 None
390 },
391 nsStyleImageType::eStyleImageType_Image => {
392 let url = self.get_image_url();
393 if self.mCropRect.mPtr.is_null() {
394 Some(GenericImage::Url(url))
395 } else {
396 let ref rect = *self.mCropRect.mPtr;
397 match (NumberOrPercentage::from_gecko_style_coord(&rect.data_at(0)),
398 NumberOrPercentage::from_gecko_style_coord(&rect.data_at(1)),
399 NumberOrPercentage::from_gecko_style_coord(&rect.data_at(2)),
400 NumberOrPercentage::from_gecko_style_coord(&rect.data_at(3))) {
401 (Some(top), Some(right), Some(bottom), Some(left)) =>
402 Some(GenericImage::Rect(Box::new(MozImageRect { url, top, right, bottom, left } ))),
403 _ => {
404 debug_assert!(false, "mCropRect could not convert to NumberOrPercentage");
405 None
406 }
407 }
408 }
409 },
410 nsStyleImageType::eStyleImageType_Gradient => {
411 Some(GenericImage::Gradient(self.get_gradient()))
412 },
413 nsStyleImageType::eStyleImageType_Element => {
414 use gecko_string_cache::Atom;
415 let atom = Gecko_GetImageElement(self);
416 Some(GenericImage::Element(Atom::from(atom)))
417 },
418 _ => panic!("Unexpected image type")
419 }
420 }
421
get_image_url(self: &nsStyleImage) -> ComputedImageUrl422 unsafe fn get_image_url(self: &nsStyleImage) -> ComputedImageUrl {
423 use gecko_bindings::bindings::Gecko_GetURLValue;
424 let url_value = Gecko_GetURLValue(self);
425 ComputedImageUrl::from_url_value_data(url_value.as_ref().unwrap())
426 .expect("Could not convert to ComputedUrl")
427 }
428
get_gradient(self: &nsStyleImage) -> Box<Gradient>429 unsafe fn get_gradient(self: &nsStyleImage) -> Box<Gradient> {
430 use gecko::values::convert_nscolor_to_rgba;
431 use gecko_bindings::bindings::Gecko_GetGradientImageValue;
432 use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_CIRCULAR, NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL};
433 use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_LINEAR, NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER};
434 use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE};
435 use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE};
436 use values::computed::{Length, LengthOrPercentage};
437 use values::computed::image::LineDirection;
438 use values::computed::position::Position;
439 use values::generics::image::{ColorStop, CompatMode, Circle, Ellipse, EndingShape, GradientKind, ShapeExtent};
440 use values::specified::position::{X, Y};
441
442 let gecko_gradient = Gecko_GetGradientImageValue(self).as_ref().unwrap();
443 let angle = Angle::from_gecko_style_coord(&gecko_gradient.mAngle);
444 let horizontal_style = LengthOrPercentage::from_gecko_style_coord(&gecko_gradient.mBgPosX);
445 let vertical_style = LengthOrPercentage::from_gecko_style_coord(&gecko_gradient.mBgPosY);
446
447 let kind = match gecko_gradient.mShape as u32 {
448 NS_STYLE_GRADIENT_SHAPE_LINEAR => {
449 let line_direction = match (angle, horizontal_style, vertical_style) {
450 (Some(a), None, None) => LineDirection::Angle(a),
451 (None, Some(horizontal), Some(vertical)) => {
452 let horizontal_as_corner = match horizontal {
453 LengthOrPercentage::Percentage(percentage) => {
454 if percentage.0 == 0.0 {
455 Some(X::Left)
456 } else if percentage.0 == 1.0 {
457 Some(X::Right)
458 } else {
459 None
460 }
461 },
462 _ => None
463 };
464 let vertical_as_corner = match vertical {
465 LengthOrPercentage::Percentage(percentage) => {
466 if percentage.0 == 0.0 {
467 Some(Y::Top)
468 } else if percentage.0 == 1.0 {
469 Some(Y::Bottom)
470 } else {
471 None
472 }
473 },
474 _ => None
475 };
476
477 match (horizontal_as_corner, vertical_as_corner) {
478 (Some(hc), Some(vc)) => LineDirection::Corner(hc, vc),
479 _ => LineDirection::MozPosition(
480 Some(Position { horizontal, vertical }), None)
481 }
482 },
483 (Some(_), Some(horizontal), Some(vertical)) =>
484 LineDirection::MozPosition(
485 Some(Position { horizontal, vertical }), angle),
486 _ => {
487 debug_assert!(horizontal_style.is_none() && vertical_style.is_none(),
488 "Unexpected linear gradient direction");
489 LineDirection::MozPosition(None, None)
490 }
491 };
492 GradientKind::Linear(line_direction)
493 },
494 _ => {
495 let gecko_size_to_keyword = |gecko_size| {
496 match gecko_size {
497 NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE => ShapeExtent::ClosestSide,
498 NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE => ShapeExtent::FarthestSide,
499 NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER => ShapeExtent::ClosestCorner,
500 NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER => ShapeExtent::FarthestCorner,
501 // FIXME: We should support ShapeExtent::Contain and ShapeExtent::Cover.
502 // But we can't choose those yet since Gecko does not support both values.
503 // https://bugzilla.mozilla.org/show_bug.cgi?id=1217664
504 _ => panic!("Found unexpected gecko_size"),
505 }
506 };
507
508 let shape = match gecko_gradient.mShape as u32 {
509 NS_STYLE_GRADIENT_SHAPE_CIRCULAR => {
510 let circle = match gecko_gradient.mSize as u32 {
511 NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE => {
512 let radius = Length::from_gecko_style_coord(&gecko_gradient.mRadiusX)
513 .expect("mRadiusX could not convert to Length");
514 debug_assert_eq!(radius,
515 Length::from_gecko_style_coord(&gecko_gradient.mRadiusY).unwrap());
516 Circle::Radius(radius)
517 },
518 size => Circle::Extent(gecko_size_to_keyword(size))
519 };
520 EndingShape::Circle(circle)
521 },
522 NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL => {
523 let length_percentage_keyword = match gecko_gradient.mSize as u32 {
524 NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE => {
525 match (LengthOrPercentage::from_gecko_style_coord(&gecko_gradient.mRadiusX),
526 LengthOrPercentage::from_gecko_style_coord(&gecko_gradient.mRadiusY)) {
527 (Some(x), Some(y)) => Ellipse::Radii(x, y),
528 _ => {
529 debug_assert!(false,
530 "mRadiusX, mRadiusY could not convert to LengthOrPercentage");
531 Ellipse::Radii(LengthOrPercentage::zero(),
532 LengthOrPercentage::zero())
533 }
534 }
535 },
536 size => Ellipse::Extent(gecko_size_to_keyword(size))
537 };
538 EndingShape::Ellipse(length_percentage_keyword)
539 },
540 _ => panic!("Found unexpected mShape"),
541 };
542
543 let position = match (horizontal_style, vertical_style) {
544 (Some(horizontal), Some(vertical)) => Position { horizontal, vertical },
545 _ => {
546 debug_assert!(false,
547 "mRadiusX, mRadiusY could not convert to LengthOrPercentage");
548 Position {
549 horizontal: LengthOrPercentage::zero(),
550 vertical: LengthOrPercentage::zero()
551 }
552 }
553 };
554
555 GradientKind::Radial(shape, position, angle)
556 }
557 };
558
559 let items = gecko_gradient.mStops.iter().map(|ref stop| {
560 if stop.mIsInterpolationHint {
561 GradientItem::InterpolationHint(
562 LengthOrPercentage::from_gecko_style_coord(&stop.mLocation)
563 .expect("mLocation could not convert to LengthOrPercentage")
564 )
565 } else {
566 GradientItem::ColorStop(ColorStop {
567 color: convert_nscolor_to_rgba(stop.mColor),
568 position: LengthOrPercentage::from_gecko_style_coord(&stop.mLocation)
569 })
570 }
571 }).collect();
572
573 let compat_mode =
574 if gecko_gradient.mMozLegacySyntax {
575 CompatMode::Moz
576 } else if gecko_gradient.mLegacySyntax {
577 CompatMode::WebKit
578 } else {
579 CompatMode::Modern
580 };
581
582 Box::new(Gradient { items, repeating: gecko_gradient.mRepeating, kind, compat_mode })
583 }
584 }
585
586 pub mod basic_shape {
587 //! Conversions from and to CSS shape representations.
588
589 use gecko::values::GeckoStyleCoordConvertible;
590 use gecko_bindings::structs;
591 use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleFillRule};
592 use gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource, StyleShapeSourceType};
593 use gecko_bindings::structs::{nsStyleCoord, nsStyleCorners};
594 use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
595 use std::borrow::Borrow;
596 use values::computed::ComputedUrl;
597 use values::computed::basic_shape::{BasicShape, ClippingShape, FloatAreaShape, ShapeRadius};
598 use values::computed::border::{BorderCornerRadius, BorderRadius};
599 use values::computed::length::LengthOrPercentage;
600 use values::computed::position;
601 use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon};
602 use values::generics::basic_shape::{Circle, Ellipse, FillRule};
603 use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
604 use values::generics::border::BorderRadius as GenericBorderRadius;
605 use values::generics::rect::Rect;
606
607 impl StyleShapeSource {
608 /// Convert StyleShapeSource to ShapeSource except URL and Image
609 /// types.
into_shape_source<ReferenceBox, ImageOrUrl>( &self ) -> Option<ShapeSource<BasicShape, ReferenceBox, ImageOrUrl>> where ReferenceBox: From<StyleGeometryBox>610 fn into_shape_source<ReferenceBox, ImageOrUrl>(
611 &self
612 ) -> Option<ShapeSource<BasicShape, ReferenceBox, ImageOrUrl>>
613 where
614 ReferenceBox: From<StyleGeometryBox>
615 {
616 match self.mType {
617 StyleShapeSourceType::None => Some(ShapeSource::None),
618 StyleShapeSourceType::Box => Some(ShapeSource::Box(self.mReferenceBox.into())),
619 StyleShapeSourceType::Shape => {
620 let other_shape = unsafe { &*self.mBasicShape.mPtr };
621 let shape = other_shape.into();
622 let reference_box = if self.mReferenceBox == StyleGeometryBox::NoBox {
623 None
624 } else {
625 Some(self.mReferenceBox.into())
626 };
627 Some(ShapeSource::Shape(shape, reference_box))
628 },
629 StyleShapeSourceType::URL | StyleShapeSourceType::Image => None,
630 }
631 }
632 }
633
634 impl<'a> From<&'a StyleShapeSource> for ClippingShape
635 {
from(other: &'a StyleShapeSource) -> Self636 fn from(other: &'a StyleShapeSource) -> Self {
637 match other.mType {
638 StyleShapeSourceType::URL => {
639 unsafe {
640 let shape_image = &*other.mShapeImage.mPtr;
641 let other_url = &(**shape_image.__bindgen_anon_1.mURLValue.as_ref());
642 let url = ComputedUrl::from_url_value_data(&other_url._base).unwrap();
643 ShapeSource::ImageOrUrl(url)
644 }
645 },
646 StyleShapeSourceType::Image => {
647 unreachable!("ClippingShape doesn't support Image!");
648 }
649 _ => other.into_shape_source().expect("Couldn't convert to StyleSource!")
650 }
651 }
652 }
653
654 impl<'a> From<&'a StyleShapeSource> for FloatAreaShape
655 {
from(other: &'a StyleShapeSource) -> Self656 fn from(other: &'a StyleShapeSource) -> Self {
657 match other.mType {
658 StyleShapeSourceType::URL => {
659 unreachable!("FloatAreaShape doesn't support URL!");
660 },
661 StyleShapeSourceType::Image => {
662 unsafe {
663 let shape_image = &*other.mShapeImage.mPtr;
664 let image = shape_image.into_image().expect("Cannot convert to Image");
665 ShapeSource::ImageOrUrl(image)
666 }
667 }
668 _ => other.into_shape_source().expect("Couldn't convert to StyleSource!")
669 }
670 }
671 }
672
673 impl<'a> From<&'a StyleBasicShape> for BasicShape {
from(other: &'a StyleBasicShape) -> Self674 fn from(other: &'a StyleBasicShape) -> Self {
675 match other.mType {
676 StyleBasicShapeType::Inset => {
677 let t = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[0]);
678 let r = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[1]);
679 let b = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[2]);
680 let l = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[3]);
681 let round = (&other.mRadius).into();
682 let rect = Rect::new(
683 t.expect("inset() offset should be a length, percentage, or calc value"),
684 r.expect("inset() offset should be a length, percentage, or calc value"),
685 b.expect("inset() offset should be a length, percentage, or calc value"),
686 l.expect("inset() offset should be a length, percentage, or calc value"),
687 );
688 GenericBasicShape::Inset(InsetRect {
689 rect: rect,
690 round: Some(round),
691 })
692 }
693 StyleBasicShapeType::Circle => {
694 GenericBasicShape::Circle(Circle {
695 radius: (&other.mCoordinates[0]).into(),
696 position: (&other.mPosition).into()
697 })
698 }
699 StyleBasicShapeType::Ellipse => {
700 GenericBasicShape::Ellipse(Ellipse {
701 semiaxis_x: (&other.mCoordinates[0]).into(),
702 semiaxis_y: (&other.mCoordinates[1]).into(),
703 position: (&other.mPosition).into()
704 })
705 }
706 StyleBasicShapeType::Polygon => {
707 let fill_rule = if other.mFillRule == StyleFillRule::Evenodd {
708 FillRule::Evenodd
709 } else {
710 FillRule::Nonzero
711 };
712 let mut coords = Vec::with_capacity(other.mCoordinates.len() / 2);
713 for i in 0..(other.mCoordinates.len() / 2) {
714 let x = 2 * i;
715 let y = x + 1;
716 coords.push((LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[x])
717 .expect("polygon() coordinate should be a length, percentage, or calc value"),
718 LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[y])
719 .expect("polygon() coordinate should be a length, percentage, or calc value")
720 ))
721 }
722 GenericBasicShape::Polygon(Polygon {
723 fill: fill_rule,
724 coordinates: coords,
725 })
726 }
727 }
728 }
729 }
730
731 impl<'a> From<&'a nsStyleCorners> for BorderRadius {
from(other: &'a nsStyleCorners) -> Self732 fn from(other: &'a nsStyleCorners) -> Self {
733 let get_corner = |index| {
734 BorderCornerRadius::new(
735 LengthOrPercentage::from_gecko_style_coord(&other.data_at(index))
736 .expect("<border-radius> should be a length, percentage, or calc value"),
737 LengthOrPercentage::from_gecko_style_coord(&other.data_at(index + 1))
738 .expect("<border-radius> should be a length, percentage, or calc value"))
739 };
740
741 GenericBorderRadius {
742 top_left: get_corner(0),
743 top_right: get_corner(2),
744 bottom_right: get_corner(4),
745 bottom_left: get_corner(6),
746 }
747 }
748 }
749
750 // Can't be a From impl since we need to set an existing
751 // nsStyleCorners, not create a new one
752 impl BorderRadius {
753 /// Set this `BorderRadius` into a given `nsStyleCoord`.
set_corners(&self, other: &mut nsStyleCorners)754 pub fn set_corners(&self, other: &mut nsStyleCorners) {
755 let mut set_corner = |field: &BorderCornerRadius, index| {
756 field.0.width().to_gecko_style_coord(&mut other.data_at_mut(index));
757 field.0.height().to_gecko_style_coord(&mut other.data_at_mut(index + 1));
758 };
759 set_corner(&self.top_left, 0);
760 set_corner(&self.top_right, 2);
761 set_corner(&self.bottom_right, 4);
762 set_corner(&self.bottom_left, 6);
763 }
764 }
765
766 /// We use None for a nonexistant radius, but Gecko uses (0 0 0 0 / 0 0 0 0)
set_corners_from_radius(radius: Option<BorderRadius>, other: &mut nsStyleCorners)767 pub fn set_corners_from_radius(radius: Option<BorderRadius>, other: &mut nsStyleCorners) {
768 if let Some(radius) = radius {
769 radius.set_corners(other);
770 } else {
771 for i in 0..8 {
772 other.data_at_mut(i).set_value(CoordDataValue::Coord(0));
773 }
774 }
775 }
776
777 // Can't be a From impl since we need to set an existing
778 // Position, not create a new one
779 impl From<position::Position> for structs::Position {
from(other: position::Position) -> Self780 fn from(other: position::Position) -> Self {
781 structs::Position {
782 mXPosition: other.horizontal.into(),
783 mYPosition: other.vertical.into()
784 }
785 }
786 }
787
788 impl<'a> From<&'a nsStyleCoord> for ShapeRadius {
from(other: &'a nsStyleCoord) -> Self789 fn from(other: &'a nsStyleCoord) -> Self {
790 let other = other.borrow();
791 ShapeRadius::from_gecko_style_coord(other)
792 .expect("<shape-radius> should be a length, percentage, calc, or keyword value")
793 }
794 }
795
796 impl<'a> From<&'a structs::Position> for position::Position {
from(other: &'a structs::Position) -> Self797 fn from(other: &'a structs::Position) -> Self {
798 position::Position {
799 horizontal: other.mXPosition.into(),
800 vertical: other.mYPosition.into(),
801 }
802 }
803 }
804
805 impl From<ShapeBox> for StyleGeometryBox {
from(reference: ShapeBox) -> Self806 fn from(reference: ShapeBox) -> Self {
807 use gecko_bindings::structs::StyleGeometryBox::*;
808 match reference {
809 ShapeBox::ContentBox => ContentBox,
810 ShapeBox::PaddingBox => PaddingBox,
811 ShapeBox::BorderBox => BorderBox,
812 ShapeBox::MarginBox => MarginBox,
813 }
814 }
815 }
816
817 impl From<GeometryBox> for StyleGeometryBox {
from(reference: GeometryBox) -> Self818 fn from(reference: GeometryBox) -> Self {
819 use gecko_bindings::structs::StyleGeometryBox::*;
820 match reference {
821 GeometryBox::ShapeBox(shape_box) => From::from(shape_box),
822 GeometryBox::FillBox => FillBox,
823 GeometryBox::StrokeBox => StrokeBox,
824 GeometryBox::ViewBox => ViewBox,
825 }
826 }
827 }
828
829 // Will panic on NoBox
830 // Ideally these would be implemented on Option<T>,
831 // but coherence doesn't like that and TryFrom isn't stable
832 impl From<StyleGeometryBox> for GeometryBox {
from(reference: StyleGeometryBox) -> Self833 fn from(reference: StyleGeometryBox) -> Self {
834 use gecko_bindings::structs::StyleGeometryBox::*;
835 match reference {
836 ContentBox => GeometryBox::ShapeBox(ShapeBox::ContentBox),
837 PaddingBox => GeometryBox::ShapeBox(ShapeBox::PaddingBox),
838 BorderBox => GeometryBox::ShapeBox(ShapeBox::BorderBox),
839 MarginBox => GeometryBox::ShapeBox(ShapeBox::MarginBox),
840 FillBox => GeometryBox::FillBox,
841 StrokeBox => GeometryBox::StrokeBox,
842 ViewBox => GeometryBox::ViewBox,
843 _ => panic!("Unexpected StyleGeometryBox while converting to GeometryBox"),
844 }
845 }
846 }
847
848 impl From<StyleGeometryBox> for ShapeBox {
from(reference: StyleGeometryBox) -> Self849 fn from(reference: StyleGeometryBox) -> Self {
850 use gecko_bindings::structs::StyleGeometryBox::*;
851 match reference {
852 ContentBox => ShapeBox::ContentBox,
853 PaddingBox => ShapeBox::PaddingBox,
854 BorderBox => ShapeBox::BorderBox,
855 MarginBox => ShapeBox::MarginBox,
856 _ => panic!("Unexpected StyleGeometryBox while converting to ShapeBox"),
857 }
858 }
859 }
860 }
861
862 impl From<RulesMutateError> for nsresult {
from(other: RulesMutateError) -> Self863 fn from(other: RulesMutateError) -> Self {
864 match other {
865 RulesMutateError::Syntax => nsresult::NS_ERROR_DOM_SYNTAX_ERR,
866 RulesMutateError::IndexSize => nsresult::NS_ERROR_DOM_INDEX_SIZE_ERR,
867 RulesMutateError::HierarchyRequest => nsresult::NS_ERROR_DOM_HIERARCHY_REQUEST_ERR,
868 RulesMutateError::InvalidState => nsresult::NS_ERROR_DOM_INVALID_STATE_ERR,
869 }
870 }
871 }
872
873 impl From<Origin> for SheetType {
from(other: Origin) -> Self874 fn from(other: Origin) -> Self {
875 match other {
876 Origin::UserAgent => SheetType::Agent,
877 Origin::Author => SheetType::Doc,
878 Origin::User => SheetType::User,
879 }
880 }
881 }
882
883 impl TrackSize<LengthOrPercentage> {
884 /// Return TrackSize from given two nsStyleCoord
from_gecko_style_coords<T: CoordData>(gecko_min: &T, gecko_max: &T) -> Self885 pub fn from_gecko_style_coords<T: CoordData>(gecko_min: &T, gecko_max: &T) -> Self {
886 use gecko_bindings::structs::root::nsStyleUnit;
887 use values::computed::length::LengthOrPercentage;
888 use values::generics::grid::{TrackBreadth, TrackSize};
889
890 if gecko_min.unit() == nsStyleUnit::eStyleUnit_None {
891 debug_assert!(gecko_max.unit() == nsStyleUnit::eStyleUnit_Coord ||
892 gecko_max.unit() == nsStyleUnit::eStyleUnit_Percent ||
893 gecko_max.unit() == nsStyleUnit::eStyleUnit_Calc);
894 return TrackSize::FitContent(LengthOrPercentage::from_gecko_style_coord(gecko_max)
895 .expect("gecko_max could not convert to LengthOrPercentage"));
896 }
897
898 let min = TrackBreadth::from_gecko_style_coord(gecko_min)
899 .expect("gecko_min could not convert to TrackBreadth");
900 let max = TrackBreadth::from_gecko_style_coord(gecko_max)
901 .expect("gecko_max could not convert to TrackBreadth");
902 if min == max {
903 TrackSize::Breadth(max)
904 } else {
905 TrackSize::Minmax(min, max)
906 }
907 }
908
909 /// Save TrackSize to given gecko fields.
to_gecko_style_coords<T: CoordDataMut>(&self, gecko_min: &mut T, gecko_max: &mut T)910 pub fn to_gecko_style_coords<T: CoordDataMut>(&self, gecko_min: &mut T, gecko_max: &mut T) {
911 use values::generics::grid::TrackSize;
912
913 match *self {
914 TrackSize::FitContent(ref lop) => {
915 // Gecko sets min value to None and max value to the actual value in fit-content
916 // https://dxr.mozilla.org/mozilla-central/rev/0eef1d5/layout/style/nsRuleNode.cpp#8221
917 gecko_min.set_value(CoordDataValue::None);
918 lop.to_gecko_style_coord(gecko_max);
919 },
920 TrackSize::Breadth(ref breadth) => {
921 // Set the value to both fields if there's one breadth value
922 // https://dxr.mozilla.org/mozilla-central/rev/0eef1d5/layout/style/nsRuleNode.cpp#8230
923 breadth.to_gecko_style_coord(gecko_min);
924 breadth.to_gecko_style_coord(gecko_max);
925 },
926 TrackSize::Minmax(ref min, ref max) => {
927 min.to_gecko_style_coord(gecko_min);
928 max.to_gecko_style_coord(gecko_max);
929 },
930 }
931 }
932 }
933
934 impl TrackListValue<LengthOrPercentage, Integer> {
935 /// Return TrackSize from given two nsStyleCoord
from_gecko_style_coords<T: CoordData>(gecko_min: &T, gecko_max: &T) -> Self936 pub fn from_gecko_style_coords<T: CoordData>(gecko_min: &T, gecko_max: &T) -> Self {
937 TrackListValue::TrackSize(TrackSize::from_gecko_style_coords(gecko_min, gecko_max))
938 }
939
940 /// Save TrackSize to given gecko fields.
to_gecko_style_coords<T: CoordDataMut>(&self, gecko_min: &mut T, gecko_max: &mut T)941 pub fn to_gecko_style_coords<T: CoordDataMut>(&self, gecko_min: &mut T, gecko_max: &mut T) {
942 use values::generics::grid::TrackListValue;
943
944 match *self {
945 TrackListValue::TrackSize(ref size) => size.to_gecko_style_coords(gecko_min, gecko_max),
946 _ => unreachable!("Should only transform from track-size computed values"),
947 }
948 }
949 }
950
951 impl<T> Rect<T> where T: GeckoStyleCoordConvertible {
952 /// Convert this generic Rect to given Gecko fields.
to_gecko_rect(&self, sides: &mut ::gecko_bindings::structs::nsStyleSides)953 pub fn to_gecko_rect(&self, sides: &mut ::gecko_bindings::structs::nsStyleSides) {
954 self.0.to_gecko_style_coord(&mut sides.data_at_mut(0));
955 self.1.to_gecko_style_coord(&mut sides.data_at_mut(1));
956 self.2.to_gecko_style_coord(&mut sides.data_at_mut(2));
957 self.3.to_gecko_style_coord(&mut sides.data_at_mut(3));
958 }
959
960 /// Convert from given Gecko data to generic Rect.
from_gecko_rect(sides: &::gecko_bindings::structs::nsStyleSides) -> Option<::values::generics::rect::Rect<T>>961 pub fn from_gecko_rect(sides: &::gecko_bindings::structs::nsStyleSides)
962 -> Option<::values::generics::rect::Rect<T>> {
963 use values::generics::rect::Rect;
964
965 Some(
966 Rect::new(
967 T::from_gecko_style_coord(&sides.data_at(0)).expect("coord[0] cound not convert"),
968 T::from_gecko_style_coord(&sides.data_at(1)).expect("coord[1] cound not convert"),
969 T::from_gecko_style_coord(&sides.data_at(2)).expect("coord[2] cound not convert"),
970 T::from_gecko_style_coord(&sides.data_at(3)).expect("coord[3] cound not convert")
971 )
972 )
973 }
974 }
975
976 impl<L> VerticalAlign<L> {
977 /// Converts an enumerated value coming from Gecko to a `VerticalAlign<L>`.
from_gecko_keyword(value: u32) -> Self978 pub fn from_gecko_keyword(value: u32) -> Self {
979 match value {
980 structs::NS_STYLE_VERTICAL_ALIGN_BASELINE => VerticalAlign::Baseline,
981 structs::NS_STYLE_VERTICAL_ALIGN_SUB => VerticalAlign::Sub,
982 structs::NS_STYLE_VERTICAL_ALIGN_SUPER => VerticalAlign::Super,
983 structs::NS_STYLE_VERTICAL_ALIGN_TOP => VerticalAlign::Top,
984 structs::NS_STYLE_VERTICAL_ALIGN_TEXT_TOP => VerticalAlign::TextTop,
985 structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE => VerticalAlign::Middle,
986 structs::NS_STYLE_VERTICAL_ALIGN_BOTTOM => VerticalAlign::Bottom,
987 structs::NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM => VerticalAlign::TextBottom,
988 structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE => {
989 VerticalAlign::MozMiddleWithBaseline
990 },
991 _ => panic!("unexpected enumerated value for vertical-align"),
992 }
993 }
994 }
995
996 impl TextAlign {
997 /// Obtain a specified value from a Gecko keyword value
998 ///
999 /// Intended for use with presentation attributes, not style structs
from_gecko_keyword(kw: u32) -> Self1000 pub fn from_gecko_keyword(kw: u32) -> Self {
1001 match kw {
1002 structs::NS_STYLE_TEXT_ALIGN_LEFT => TextAlign::Left,
1003 structs::NS_STYLE_TEXT_ALIGN_RIGHT => TextAlign::Right,
1004 structs::NS_STYLE_TEXT_ALIGN_CENTER => TextAlign::Center,
1005 structs::NS_STYLE_TEXT_ALIGN_JUSTIFY => TextAlign::Justify,
1006 structs::NS_STYLE_TEXT_ALIGN_MOZ_LEFT => TextAlign::MozLeft,
1007 structs::NS_STYLE_TEXT_ALIGN_MOZ_RIGHT => TextAlign::MozRight,
1008 structs::NS_STYLE_TEXT_ALIGN_MOZ_CENTER => TextAlign::MozCenter,
1009 structs::NS_STYLE_TEXT_ALIGN_CHAR => TextAlign::Char,
1010 structs::NS_STYLE_TEXT_ALIGN_END => TextAlign::End,
1011 _ => panic!("Found unexpected value in style struct for text-align property"),
1012 }
1013 }
1014 }
1015
1016 /// Convert to String from given chars pointer.
string_from_chars_pointer(p: *const u16) -> String1017 pub unsafe fn string_from_chars_pointer(p: *const u16) -> String {
1018 use std::slice;
1019 let mut length = 0;
1020 let mut iter = p;
1021 while *iter != 0 {
1022 length += 1;
1023 iter = iter.offset(1);
1024 }
1025 let char_vec = slice::from_raw_parts(p, length as usize);
1026 String::from_utf16_lossy(char_vec)
1027 }
1028