tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

wrench.rs (25900B)


      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 use std::collections::HashMap;
      6 
      7 use webrender::scene::Scene;
      8 use webrender_api::{units::LayoutRect, BorderDetails, BorderStyle, BuiltDisplayList};
      9 use webrender_api::{ColorF, DisplayItem, PipelineId, PropertyBinding, SpatialId};
     10 use webrender_api::{ClipId, SpatialTreeItem, ClipChainId};
     11 
     12 fn color_to_string(
     13    color: ColorF,
     14 ) -> String {
     15    match (color.r, color.g, color.b, color.a) {
     16        (1.0, 0.0, 0.0, 1.0) => "red".into(),
     17        _ => {
     18            format!("{} {} {} {}",
     19                color.r * 255.0,
     20                color.g * 255.0,
     21                color.b * 255.0,
     22                color.a,
     23            )
     24        }
     25    }
     26 }
     27 
     28 fn color_to_string_array(
     29    color: ColorF,
     30 ) -> String {
     31    match (color.r, color.g, color.b, color.a) {
     32        (1.0, 0.0, 0.0, 1.0) => "red".into(),
     33        _ => {
     34            format!("{}, {}, {}, {}",
     35                color.r * 255.0,
     36                color.g * 255.0,
     37                color.b * 255.0,
     38                color.a,
     39            )
     40        }
     41    }
     42 }
     43 
     44 fn style_to_string(
     45    style: BorderStyle,
     46 ) -> String {
     47    match style {
     48        BorderStyle::None => "none",
     49        BorderStyle::Solid => "solid",
     50        BorderStyle::Double => "double",
     51        BorderStyle::Dotted => "dotted",
     52        BorderStyle::Dashed => "dashed",
     53        BorderStyle::Hidden => "hidden",
     54        BorderStyle::Ridge => "ridge",
     55        BorderStyle::Inset => "inset",
     56        BorderStyle::Outset => "outset",
     57        BorderStyle::Groove => "groove",
     58    }.into()
     59 }
     60 
     61 #[derive(Debug)]
     62 enum SpatialNodeKind {
     63    Reference {
     64    },
     65    Scroll {
     66    },
     67    Sticky {
     68    },
     69 }
     70 
     71 #[derive(Debug)]
     72 struct SpatialNode {
     73    wrench_id: u64,
     74 }
     75 
     76 struct YamlWriter {
     77    out: String,
     78    indent: String,
     79    spatial_nodes: HashMap<SpatialId, SpatialNode>,
     80    clip_id_map: HashMap<ClipId, u64>,
     81    clipchain_id_map: HashMap<ClipChainId, u64>,
     82    next_wrench_id: u64,
     83 }
     84 
     85 impl YamlWriter {
     86    fn new() -> Self {
     87        YamlWriter {
     88            out: String::new(),
     89            indent: String::new(),
     90            spatial_nodes: HashMap::new(),
     91            next_wrench_id: 2,
     92            clip_id_map: HashMap::new(),
     93            clipchain_id_map: HashMap::new(),
     94        }
     95    }
     96 
     97    fn push_level(&mut self) {
     98        self.indent.push_str("  ");
     99    }
    100 
    101    fn pop_level(&mut self) {
    102        self.indent.truncate(self.indent.len() - 2);
    103    }
    104 
    105    fn add_clip_id(
    106        &mut self,
    107        clip_id: ClipId
    108    ) -> u64 {
    109        let id = self.next_wrench_id;
    110        self.next_wrench_id += 1;
    111 
    112        let _prev = self.clip_id_map.insert(clip_id, id);
    113        assert!(_prev.is_none());
    114 
    115        id
    116    }
    117 
    118    fn add_clipchain_id(
    119        &mut self,
    120        clipchain_id: ClipChainId
    121    ) -> u64 {
    122        let id = self.next_wrench_id;
    123        self.next_wrench_id += 1;
    124 
    125        let _prev = self.clipchain_id_map.insert(clipchain_id, id);
    126        assert!(_prev.is_none());
    127 
    128        id
    129    }
    130 
    131    fn add_and_write_spatial_node(
    132        &mut self,
    133        spatial_id: SpatialId,
    134        parent: SpatialId,
    135        kind: SpatialNodeKind,
    136    ) {
    137        match kind {
    138            SpatialNodeKind::Reference {} => {
    139                self.write_line("- type: reference-frame");
    140                self.push_level();
    141                self.write_line(&format!("id: {}", self.next_wrench_id));
    142                if let Some(parent) = self.spatial_nodes.get(&parent) {
    143                    self.write_line(&format!("spatial-id: {}", parent.wrench_id));
    144                }
    145                self.pop_level();
    146            }
    147            SpatialNodeKind::Scroll {} => {
    148                let parent_id = self.spatial_nodes[&parent].wrench_id;
    149 
    150                self.write_line("- type: scroll-frame");
    151                self.push_level();
    152                self.write_line(&format!("id: {}", self.next_wrench_id));
    153                self.write_bounds(LayoutRect::zero());
    154                self.write_line(&format!("spatial-id: {}", parent_id));
    155                self.pop_level();
    156            }
    157            SpatialNodeKind::Sticky {} => {
    158                let parent_id = self.spatial_nodes[&parent].wrench_id;
    159 
    160                self.write_line("- type: sticky-frame");
    161                self.push_level();
    162                self.write_line(&format!("id: {}", self.next_wrench_id));
    163                self.write_line(&format!("spatial-id: {}", parent_id));
    164                self.write_bounds(LayoutRect::zero());
    165                self.pop_level();
    166            }
    167        }
    168 
    169        let _prev = self.spatial_nodes.insert(
    170            spatial_id,
    171            SpatialNode {
    172                wrench_id: self.next_wrench_id,
    173            },
    174        );
    175        assert!(_prev.is_none());
    176        self.next_wrench_id += 1;
    177    }
    178 
    179    fn write_color(
    180        &mut self,
    181        color: ColorF,
    182    ) {
    183        self.write_line(
    184            &format!("color: {}", color_to_string(color))
    185        );
    186    }
    187 
    188    fn write_rect(
    189        &mut self,
    190        tag: &str,
    191        rect: LayoutRect,
    192    ) {
    193        self.write_line(
    194            &format!("{}: {} {} {} {}",
    195                tag,
    196                rect.min.x,
    197                rect.min.y,
    198                rect.width(),
    199                rect.height(),
    200            )
    201        );
    202    }
    203 
    204    fn write_bounds(
    205        &mut self,
    206        bounds: LayoutRect,
    207    ) {
    208        self.write_rect("bounds", bounds);
    209    }
    210 
    211    fn maybe_write_clip_rect(
    212        &mut self,
    213        bounds: LayoutRect,
    214        clip_rect: LayoutRect,
    215    ) {
    216        if bounds != clip_rect {
    217            self.write_rect("clip-rect", clip_rect);
    218        }
    219    }
    220 
    221    fn create_savepoint(
    222        &mut self,
    223    ) -> (usize, usize) {
    224        (self.out.len(), self.indent.len())
    225    }
    226 
    227    fn restore_savepoint(
    228        &mut self,
    229        p: (usize, usize),
    230    ) {
    231        self.out.truncate(p.0);
    232        self.indent.truncate(p.1);
    233    }
    234 
    235    fn write_line(
    236        &mut self,
    237        s: &str,
    238    ) {
    239        self.out.push_str(&self.indent);
    240        self.out.push_str(s);
    241        self.out.push_str("\n");
    242    }
    243 
    244    fn write_spatial_id(
    245        &mut self,
    246        id: SpatialId,
    247    ) {
    248        let spatial_node = self.spatial_nodes
    249            .get(&id)
    250            .expect(&format!("unknown spatial node {:?}", id));
    251 
    252        self.write_line(&format!("spatial-id: {}", spatial_node.wrench_id));
    253    }
    254 
    255    fn write_clip_chain_id(
    256        &mut self,
    257        id: ClipChainId,
    258    ) {
    259        if id != ClipChainId::INVALID {
    260            let clip_chain_id = self.clipchain_id_map[&id];
    261            self.write_line(&format!("clip-chain: {}", clip_chain_id));
    262        }
    263    }
    264 
    265    fn build_spatial_tree(
    266        &mut self,
    267        dl: &BuiltDisplayList,
    268        pipeline_id: PipelineId,
    269    ) {
    270        // Insert root ref + scroll frames
    271        self.add_and_write_spatial_node(
    272            SpatialId::root_reference_frame(pipeline_id),
    273            SpatialId::root_reference_frame(pipeline_id),
    274            SpatialNodeKind::Reference {  },
    275        );
    276        self.add_and_write_spatial_node(
    277            SpatialId::root_scroll_node(pipeline_id),
    278            SpatialId::root_reference_frame(pipeline_id),
    279            SpatialNodeKind::Scroll {  },
    280        );
    281 
    282        dl.iter_spatial_tree(|item| {
    283            match item {
    284                SpatialTreeItem::ScrollFrame(descriptor) => {
    285                    self.add_and_write_spatial_node(
    286                        descriptor.scroll_frame_id,
    287                        descriptor.parent_space,
    288                        SpatialNodeKind::Scroll {
    289                        },
    290                    );
    291                }
    292                SpatialTreeItem::ReferenceFrame(descriptor) => {
    293                    self.add_and_write_spatial_node(
    294                        descriptor.reference_frame.id,
    295                        descriptor.parent_spatial_id,
    296                        SpatialNodeKind::Reference {
    297                        },
    298                    );
    299                }
    300                SpatialTreeItem::StickyFrame(descriptor) => {
    301                    self.add_and_write_spatial_node(
    302                        descriptor.id,
    303                        descriptor.parent_spatial_id,
    304                        SpatialNodeKind::Sticky {
    305                        },
    306                    );
    307                }
    308                SpatialTreeItem::Invalid => {
    309                    unreachable!();
    310                }
    311            }
    312        });
    313    }
    314 
    315    fn write_pipeline(
    316        &mut self,
    317        scene: &Scene,
    318        pipeline_id: PipelineId,
    319    ) -> Result<(), String> {
    320        enum ContextKind {
    321            Root,
    322            StackingContext {
    323                // sc_info: StackingContextInfo,
    324            },
    325        }
    326        struct BuildContext {
    327            kind: ContextKind,
    328        }
    329 
    330        let pipeline = &scene.pipelines[&pipeline_id];
    331 
    332        self.build_spatial_tree(
    333            &pipeline.display_list.display_list,
    334            pipeline_id,
    335        );
    336 
    337        let mut stack = vec![BuildContext {
    338            kind: ContextKind::Root,
    339        }];
    340        let mut traversal = pipeline.display_list.iter();
    341 
    342        'outer: while let Some(bc) = stack.pop() {
    343            loop {
    344                let item = match traversal.next() {
    345                    Some(item) => item,
    346                    None => break,
    347                };
    348 
    349                match item.item() {
    350                    DisplayItem::PushStackingContext(info) => {
    351                        self.write_line("- type: stacking-context");
    352                        self.push_level();
    353                        self.write_spatial_id(info.spatial_id);
    354                        if let Some(clip_chain_id) = info.stacking_context.clip_chain_id {
    355                            self.write_clip_chain_id(clip_chain_id);
    356                        }
    357                        self.write_line(
    358                            &format!("bounds: {} {} 0 0",
    359                                0.0, //info.origin.x + info.ref_frame_offset.x,
    360                                0.0, //info.origin.y + info.ref_frame_offset.y,
    361                            )
    362                        );
    363                        self.write_line("items:");
    364                        self.push_level();
    365 
    366                        let new_context = BuildContext {
    367                            kind: ContextKind::StackingContext {
    368                                // sc_info,
    369                            },
    370                        };
    371                        stack.push(bc);
    372                        stack.push(new_context);
    373 
    374                        traversal = item.sub_iter();
    375                        continue 'outer;
    376                    }
    377                    DisplayItem::PopStackingContext => {
    378                        self.pop_level();
    379                        self.pop_level();
    380                    }
    381                    DisplayItem::Iframe(info) => {
    382                        self.write_line("- type: iframe");
    383                        self.push_level();
    384                        self.write_spatial_id(info.space_and_clip.spatial_id);
    385                        self.write_clip_chain_id(info.space_and_clip.clip_chain_id);
    386                        self.write_bounds(info.bounds);
    387                        self.maybe_write_clip_rect(info.bounds, info.clip_rect);
    388                        self.write_line(&format!("id: [{}, {}]",
    389                            info.pipeline_id.0,
    390                            info.pipeline_id.1,
    391                        ));
    392                        self.pop_level();
    393                    }
    394                    DisplayItem::Rectangle(info) => {
    395                        self.write_line("- type: rect");
    396                        self.push_level();
    397                        self.write_spatial_id(info.common.spatial_id);
    398                        self.write_clip_chain_id(info.common.clip_chain_id);
    399                        self.write_bounds(info.bounds);
    400                        self.maybe_write_clip_rect(info.bounds, info.common.clip_rect);
    401                        let color = match info.color {
    402                            PropertyBinding::Binding(..) => {
    403                                println!("WARN: Property color bindings are unsupported");
    404                                ColorF::new(1.0, 0.0, 1.0, 0.5)
    405                            }
    406                            PropertyBinding::Value(color) => {
    407                                color
    408                            }
    409                        };
    410                        if color.a > 0.0 {
    411                            self.write_color(color);
    412                        }
    413                        self.pop_level();
    414                    }
    415                    DisplayItem::Text(info) => {
    416                        self.write_line("- type: rect");
    417                        self.push_level();
    418                        self.write_spatial_id(info.common.spatial_id);
    419                        self.write_clip_chain_id(info.common.clip_chain_id);
    420                        self.write_bounds(info.bounds);
    421                        self.maybe_write_clip_rect(info.bounds, info.common.clip_rect);
    422                        self.write_color(ColorF::new(1.0, 0.0, 0.0, 0.5));
    423                        self.pop_level();
    424                    }
    425                    DisplayItem::Border(info) => {
    426                        let sp = self.create_savepoint();
    427 
    428                        self.write_line("- type: border");
    429                        self.push_level();
    430                        self.write_spatial_id(info.common.spatial_id);
    431                        self.write_clip_chain_id(info.common.clip_chain_id);
    432                        self.maybe_write_clip_rect(info.bounds, info.common.clip_rect);
    433                        self.write_bounds(info.bounds);
    434 
    435                        match info.details {
    436                            BorderDetails::Normal(border) => {
    437                                self.write_line("border-type: normal");
    438 
    439                                let colors = [
    440                                    border.top.color,
    441                                    border.right.color,
    442                                    border.bottom.color,
    443                                    border.left.color,
    444                                ];
    445 
    446                                if colors.iter().all(|c| c.a == 0.0) {
    447                                    self.restore_savepoint(sp);
    448                                    continue;
    449                                }
    450 
    451                                if colors.iter().all(|c| *c == border.top.color) {
    452                                    self.write_color(border.top.color);
    453                                } else {
    454                                    self.write_line(&format!(
    455                                            "color: [ [{}], [{}], [{}], [{}] ]",
    456                                            color_to_string_array(colors[0]),
    457                                            color_to_string_array(colors[1]),
    458                                            color_to_string_array(colors[2]),
    459                                            color_to_string_array(colors[3]),
    460                                        )
    461                                    );
    462                                }
    463 
    464                                let styles = [
    465                                    border.top.style,
    466                                    border.right.style,
    467                                    border.bottom.style,
    468                                    border.left.style,
    469                                ];
    470 
    471                                if styles.iter().all(|s| *s == border.top.style) {
    472                                    self.write_line(&format!(
    473                                            "style: {}", style_to_string(styles[0]),
    474                                        )
    475                                    );
    476                                } else {
    477                                    self.write_line(&format!(
    478                                            "style: [ {}, {}, {}, {} ]",
    479                                            style_to_string(styles[0]),
    480                                            style_to_string(styles[1]),
    481                                            style_to_string(styles[2]),
    482                                            style_to_string(styles[3]),
    483                                        )
    484                                    );
    485                                }
    486 
    487                                self.write_line("width: [1, 1, 1, 1]");
    488 
    489                                if !border.radius.is_zero() {
    490                                    self.write_line("radius: {");
    491                                    self.push_level();
    492                                    self.write_line(
    493                                        &format!("top-left: [{}, {}],",
    494                                            border.radius.top_left.width,
    495                                            border.radius.top_left.height,
    496                                        )
    497                                    );
    498                                    self.write_line(
    499                                        &format!("top-right: [{}, {}],",
    500                                            border.radius.top_right.width,
    501                                            border.radius.top_right.height,
    502                                        )
    503                                    );
    504                                    self.write_line(
    505                                        &format!("bottom-left: [{}, {}],",
    506                                            border.radius.bottom_left.width,
    507                                            border.radius.bottom_left.height,
    508                                        )
    509                                    );
    510                                    self.write_line(
    511                                        &format!("bottom-right: [{}, {}],",
    512                                            border.radius.bottom_right.width,
    513                                            border.radius.bottom_right.height,
    514                                        )
    515                                    );
    516                                    self.pop_level();
    517                                    self.write_line("}");
    518                                }
    519                            }
    520                            BorderDetails::NinePatch(..) => {
    521                                todo!();
    522                            }
    523                        }
    524 
    525                        self.pop_level();
    526                    }
    527                    DisplayItem::Image(info) => {
    528                        self.write_line("- type: image");
    529                        self.push_level();
    530                        self.write_spatial_id(info.common.spatial_id);
    531                        self.write_clip_chain_id(info.common.clip_chain_id);
    532                        self.write_bounds(info.bounds);
    533                        self.maybe_write_clip_rect(info.bounds, info.common.clip_rect);
    534                        self.write_line(
    535                            &format!("src: checkerboard(2,8,8,{},{})",
    536                                ((info.bounds.width() - 2.0) / 8.0).ceil() as i32,
    537                                ((info.bounds.height() - 2.0) / 8.0).ceil() as i32,
    538                            ),
    539                        );
    540                        self.pop_level();
    541                    }
    542                    DisplayItem::RectClip(info) => {
    543                        let clip_id = self.add_clip_id(info.id);
    544                        self.write_line("- type: clip");
    545                        self.push_level();
    546                        self.write_line(&format!("id: {}", clip_id));
    547                        self.write_spatial_id(info.spatial_id);
    548                        self.write_rect("bounds", info.clip_rect);
    549                        self.pop_level();
    550                    }
    551                    DisplayItem::ImageMaskClip(info) => {
    552                        let clip_id = self.add_clip_id(info.id);
    553                        self.write_line("- type: clip");
    554                        self.push_level();
    555                        self.write_line(&format!("id: {}", clip_id));
    556                        self.write_spatial_id(info.spatial_id);
    557                        self.write_rect("bounds", info.image_mask.rect);
    558                        self.pop_level();
    559                    }
    560                    DisplayItem::RoundedRectClip(info) => {
    561                        let clip_id = self.add_clip_id(info.id);
    562                        self.write_line("- type: clip");
    563                        self.push_level();
    564                        self.write_line(&format!("id: {}", clip_id));
    565                        self.write_spatial_id(info.spatial_id);
    566                        self.write_line("complex:");
    567                        self.push_level();
    568                        self.write_rect("- rect", info.clip.rect);
    569                        self.push_level();
    570                        self.write_line("radius: {");
    571                        self.push_level();
    572                        self.write_line(
    573                            &format!("top-left: [{}, {}],",
    574                            info.clip.radii.top_left.width,
    575                            info.clip.radii.top_left.height,
    576                        ));
    577                        self.write_line(
    578                            &format!("top-right: [{}, {}],",
    579                            info.clip.radii.top_right.width,
    580                            info.clip.radii.top_right.height,
    581                        ));
    582                        self.write_line(
    583                            &format!("bottom-right: [{}, {}],",
    584                            info.clip.radii.bottom_right.width,
    585                            info.clip.radii.bottom_right.height,
    586                        ));
    587                        self.write_line(
    588                            &format!("bottom-left: [{}, {}],",
    589                            info.clip.radii.bottom_left.width,
    590                            info.clip.radii.bottom_left.height,
    591                        ));
    592                        self.pop_level();
    593                        self.write_line("}");
    594                        self.pop_level();
    595                        self.pop_level();
    596                        self.pop_level();
    597                    }
    598                    DisplayItem::ClipChain(info) => {
    599                        let clipchain_id = self.add_clipchain_id(info.id);
    600                        self.write_line("- type: clip-chain");
    601                        self.push_level();
    602                        self.write_line(&format!("id: {}", clipchain_id));
    603                        let mut clips = String::new();
    604                        clips.push_str("clips: [");
    605                        for id in item.clip_chain_items().iter() {
    606                            clips.push_str(&format!("{}, ", self.clip_id_map[&id]))
    607                        }
    608                        clips.push_str("]");
    609                        self.write_line(&clips);
    610                        self.pop_level();
    611                    }
    612 
    613                    // TODO(gw): Ignored elements - we should as support for
    614                    //           these as needed.
    615                    DisplayItem::SetGradientStops => {}
    616                    DisplayItem::SetFilterOps => {}
    617                    DisplayItem::SetFilterData => {}
    618                    DisplayItem::SetPoints => {}
    619                    DisplayItem::PopAllShadows => {}
    620                    DisplayItem::ReuseItems(..) => {}
    621                    DisplayItem::RetainedItems(..) => {}
    622                    DisplayItem::RepeatingImage(..) => {}
    623                    DisplayItem::YuvImage(..) => {}
    624                    DisplayItem::BackdropFilter(..) => {}
    625                    DisplayItem::PushShadow(..) => {}
    626                    DisplayItem::Gradient(..) => {}
    627                    DisplayItem::RadialGradient(..) => {}
    628                    DisplayItem::ConicGradient(..) => {}
    629                    DisplayItem::Line(..) => {}
    630                    DisplayItem::HitTest(..) => {}
    631                    DisplayItem::PushReferenceFrame(..) => {}
    632                    DisplayItem::PopReferenceFrame => {}
    633                    DisplayItem::DebugMarker(..) => {}
    634                    DisplayItem::BoxShadow(..) => {}
    635                };
    636            }
    637 
    638            match bc.kind {
    639                ContextKind::Root => {}
    640                ContextKind::StackingContext { } => {
    641                    // self.pop_stacking_context(sc_info);
    642                }
    643            }
    644        }
    645 
    646        assert!(stack.is_empty());
    647 
    648        Ok(())
    649    }
    650 
    651    fn write_scene(
    652        mut self,
    653        scene: &Scene
    654    ) -> Result<String, String> {
    655        self.write_line(&format!("# process-capture"));
    656        self.write_line("---");
    657        self.write_line("root:");
    658        self.push_level();
    659        self.write_line("items:");
    660        self.push_level();
    661 
    662        if let Some(root_pipeline_id) = scene.root_pipeline_id {
    663            self.write_pipeline(scene, root_pipeline_id)?;
    664        }
    665 
    666        self.pop_level();
    667        self.pop_level();
    668        assert!(self.indent.is_empty());
    669 
    670        if scene.pipelines.len() > 1 {
    671            self.write_line("pipelines:");
    672            self.push_level();
    673            for (id, _) in &scene.pipelines {
    674                if Some(*id) == scene.root_pipeline_id {
    675                    continue;
    676                }
    677 
    678                self.write_line(&format!("- id: [{}, {}]", id.0, id.1));
    679                self.push_level();
    680                self.write_line("items:");
    681                self.push_level();
    682                self.write_pipeline(scene, *id)?;
    683                self.pop_level();
    684                self.pop_level();
    685            }
    686            self.pop_level();
    687        }
    688 
    689        Ok(self.out)
    690    }
    691 }
    692 
    693 pub fn scene_to_yaml(
    694    scene: &Scene,
    695 ) -> Result<String, String> {
    696    let writer = YamlWriter::new();
    697 
    698    writer.write_scene(scene)
    699 }