tor-browser

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

main.rs (16083B)


      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 /*
      6 
      7   An example of how to implement the Compositor trait that
      8   allows picture caching surfaces to be composited by the operating
      9   system.
     10 
     11   The current example supports DirectComposite on Windows only.
     12 
     13 */
     14 
     15 use euclid::Angle;
     16 use gleam::gl;
     17 use std::ffi::CString;
     18 use std::sync::mpsc;
     19 use webrender::{CompositorSurfaceTransform, Transaction, api::*};
     20 use webrender::api::units::*;
     21 use webrender::Device;
     22 #[cfg(target_os = "windows")]
     23 use compositor_windows as compositor;
     24 #[cfg(target_os = "linux")]
     25 use compositor_wayland as compositor;
     26 use std::{env, f32, process};
     27 
     28 // A very hacky integration with DirectComposite. It proxies calls from the compositor
     29 // interface to a simple C99 library which does the DirectComposition / D3D11 / ANGLE
     30 // interfacing. This is a very unsafe impl due to the way the window pointer is passed
     31 // around!
     32 struct DirectCompositeInterface {
     33    window: *mut compositor::Window,
     34 }
     35 
     36 impl DirectCompositeInterface {
     37    fn new(window: *mut compositor::Window) -> Self {
     38        DirectCompositeInterface { window }
     39    }
     40 }
     41 
     42 impl webrender::Compositor for DirectCompositeInterface {
     43    fn create_surface(
     44        &mut self,
     45        _device: &mut Device,
     46        id: webrender::NativeSurfaceId,
     47        _virtual_offset: DeviceIntPoint,
     48        tile_size: DeviceIntSize,
     49        is_opaque: bool,
     50    ) {
     51        compositor::create_surface(
     52            self.window,
     53            id.0,
     54            tile_size.width,
     55            tile_size.height,
     56            is_opaque,
     57        );
     58    }
     59 
     60    fn destroy_surface(&mut self, _device: &mut Device, id: webrender::NativeSurfaceId) {
     61        compositor::destroy_surface(self.window, id.0);
     62    }
     63 
     64    fn create_tile(&mut self, _device: &mut Device, id: webrender::NativeTileId) {
     65        compositor::create_tile(self.window, id.surface_id.0, id.x, id.y);
     66    }
     67 
     68    fn destroy_tile(&mut self, _device: &mut Device, id: webrender::NativeTileId) {
     69        compositor::destroy_tile(self.window, id.surface_id.0, id.x, id.y);
     70    }
     71 
     72    fn bind(
     73        &mut self,
     74        _device: &mut Device,
     75        id: webrender::NativeTileId,
     76        dirty_rect: DeviceIntRect,
     77        _valid_rect: DeviceIntRect,
     78    ) -> webrender::NativeSurfaceInfo {
     79        let (fbo_id, x, y) = compositor::bind_surface(
     80            self.window,
     81            id.surface_id.0,
     82            id.x,
     83            id.y,
     84            dirty_rect.min.x,
     85            dirty_rect.min.y,
     86            dirty_rect.width(),
     87            dirty_rect.height(),
     88        );
     89 
     90        webrender::NativeSurfaceInfo {
     91            origin: DeviceIntPoint::new(x, y),
     92            fbo_id,
     93        }
     94    }
     95 
     96    fn unbind(&mut self, _device: &mut Device) {
     97        compositor::unbind_surface(self.window);
     98    }
     99 
    100    fn begin_frame(&mut self, _device: &mut Device) {
    101        compositor::begin_transaction(self.window);
    102    }
    103 
    104    fn add_surface(
    105        &mut self,
    106        _device: &mut Device,
    107        id: webrender::NativeSurfaceId,
    108        transform: CompositorSurfaceTransform,
    109        clip_rect: DeviceIntRect,
    110        _image_rendering: ImageRendering,
    111    ) {
    112        compositor::add_surface(
    113            self.window,
    114            id.0,
    115            transform.offset.x as i32,
    116            transform.offset.y as i32,
    117            clip_rect.min.x,
    118            clip_rect.min.y,
    119            clip_rect.width(),
    120            clip_rect.height(),
    121        );
    122    }
    123 
    124    fn end_frame(&mut self, _device: &mut Device) {
    125        compositor::end_transaction(self.window);
    126    }
    127    fn create_external_surface(
    128        &mut self,
    129        _device: &mut Device,
    130        _id: webrender::NativeSurfaceId,
    131        _: bool,
    132    ) {
    133        todo!()
    134    }
    135 
    136    fn attach_external_image(
    137        &mut self,
    138        _device: &mut Device,
    139        _id: webrender::NativeSurfaceId,
    140        _external_image: ExternalImageId,
    141    ) {
    142        todo!()
    143    }
    144 
    145    fn enable_native_compositor(&mut self, _device: &mut Device, _enable: bool) {
    146        todo!()
    147    }
    148 
    149    fn deinit(&mut self, _device: &mut Device) {
    150        compositor::deinit(self.window);
    151    }
    152 
    153    fn get_capabilities(&self, _device: &mut Device) -> webrender::CompositorCapabilities {
    154        webrender::CompositorCapabilities {
    155            virtual_surface_size: 1024 * 1024,
    156            ..Default::default()
    157        }
    158    }
    159 
    160    fn invalidate_tile(
    161        &mut self,
    162        _device: &mut Device,
    163        _id: webrender::NativeTileId,
    164        _valid_rect: DeviceIntRect,
    165    ) {
    166    }
    167 
    168    fn start_compositing(
    169        &mut self,
    170        _device: &mut Device,
    171        _color: webrender::webrender_api::ColorF,
    172        _dirty_rects: &[DeviceIntRect],
    173        _opaque_rects: &[DeviceIntRect],
    174    ) {
    175    }
    176 
    177    fn create_backdrop_surface(
    178        &mut self,
    179        _device: &mut Device,
    180        _id: webrender::NativeSurfaceId,
    181        _color: webrender::webrender_api::ColorF,
    182    ) {
    183        todo!()
    184    }
    185 
    186    fn get_window_visibility(&self, _device: &mut Device) -> webrender::WindowVisibility {
    187        todo!()
    188    }
    189 }
    190 
    191 // Simplisitic implementation of the WR notifier interface to know when a frame
    192 // has been prepared and can be rendered.
    193 struct Notifier {
    194    tx: mpsc::Sender<()>,
    195 }
    196 
    197 impl Notifier {
    198    fn new(tx: mpsc::Sender<()>) -> Self {
    199        Notifier { tx }
    200    }
    201 }
    202 
    203 impl RenderNotifier for Notifier {
    204    fn clone(&self) -> Box<dyn RenderNotifier> {
    205        Box::new(Notifier {
    206            tx: self.tx.clone(),
    207        })
    208    }
    209 
    210    fn wake_up(&self, _composite_needed: bool) {}
    211 
    212    fn new_frame_ready(&self, _: DocumentId, _: FramePublishId, _params: &FrameReadyParams) {
    213        self.tx.send(()).ok();
    214    }
    215 }
    216 
    217 fn push_rotated_rect(
    218    builder: &mut DisplayListBuilder,
    219    rect: LayoutRect,
    220    color: ColorF,
    221    spatial_id: SpatialId,
    222    _root_pipeline_id: PipelineId,
    223    angle: f32,
    224    time: f32,
    225    item_key: SpatialTreeItemKey,
    226 ) {
    227    let color = color.scale_rgb(time);
    228    let rotation = LayoutTransform::rotation(
    229        0.0,
    230        0.0,
    231        1.0,
    232        Angle::radians(2.0 * std::f32::consts::PI * angle),
    233    );
    234    let center = rect.center();
    235    let transform_origin = LayoutVector3D::new(center.x, center.y, 0.0);
    236    let transform = rotation
    237        .pre_translate(-transform_origin)
    238        .then_translate(transform_origin);
    239    let spatial_id = builder.push_reference_frame(
    240        LayoutPoint::zero(),
    241        spatial_id,
    242        TransformStyle::Flat,
    243        PropertyBinding::Value(transform),
    244        ReferenceFrameKind::Transform {
    245            is_2d_scale_translation: false,
    246            should_snap: false,
    247            paired_with_perspective: false,
    248        },
    249        item_key,
    250    );
    251    builder.push_rect(
    252        &CommonItemProperties::new(
    253            rect,
    254            SpaceAndClipInfo {
    255                spatial_id,
    256                clip_chain_id: ClipChainId::INVALID,
    257            },
    258        ),
    259        rect,
    260        color,
    261    );
    262 }
    263 
    264 fn build_display_list(
    265    builder: &mut DisplayListBuilder,
    266    scroll_id: ExternalScrollId,
    267    root_pipeline_id: PipelineId,
    268    layout_size: LayoutSize,
    269    time: f32,
    270    invalidations: Invalidations,
    271 ) {
    272    let size_factor = match invalidations {
    273        Invalidations::Small => 0.1,
    274        Invalidations::Large | Invalidations::Scrolling => 1.0,
    275    };
    276 
    277    let fixed_space_info = SpaceAndClipInfo {
    278        spatial_id: SpatialId::root_scroll_node(root_pipeline_id),
    279        clip_chain_id: ClipChainId::INVALID,
    280    };
    281 
    282    let scroll_spatial_id = builder.define_scroll_frame(
    283        fixed_space_info.spatial_id,
    284        scroll_id,
    285        LayoutRect::from_size(layout_size),
    286        LayoutRect::from_size(layout_size),
    287        LayoutVector2D::zero(),
    288        APZScrollGeneration::default(),
    289        HasScrollLinkedEffect::No,
    290        SpatialTreeItemKey::new(1, 0),
    291    );
    292 
    293    builder.push_rect(
    294        &CommonItemProperties::new(
    295            LayoutRect::from_size(layout_size).inflate(-10.0, -10.0),
    296            fixed_space_info,
    297        ),
    298        LayoutRect::from_size(layout_size).inflate(-10.0, -10.0),
    299        ColorF::new(0.8, 0.8, 0.8, 1.0),
    300    );
    301 
    302    push_rotated_rect(
    303        builder,
    304        LayoutRect::from_origin_and_size(
    305            LayoutPoint::new(100.0, 100.0),
    306            LayoutSize::new(size_factor * 400.0, size_factor * 400.0),
    307        ),
    308        ColorF::new(1.0, 0.0, 0.0, 1.0),
    309        scroll_spatial_id,
    310        root_pipeline_id,
    311        time,
    312        time,
    313        SpatialTreeItemKey::new(1, 1),
    314    );
    315 
    316    push_rotated_rect(
    317        builder,
    318        LayoutRect::from_origin_and_size(
    319            LayoutPoint::new(800.0, 100.0),
    320            LayoutSize::new(size_factor * 100.0, size_factor * 600.0),
    321        ),
    322        ColorF::new(0.0, 1.0, 0.0, 1.0),
    323        fixed_space_info.spatial_id,
    324        root_pipeline_id,
    325        0.2,
    326        time,
    327        SpatialTreeItemKey::new(1, 2),
    328    );
    329 
    330    push_rotated_rect(
    331        builder,
    332        LayoutRect::from_origin_and_size(
    333            LayoutPoint::new(700.0, 200.0),
    334            LayoutSize::new(size_factor * 300.0, size_factor * 300.0),
    335        ),
    336        ColorF::new(0.0, 0.0, 1.0, 1.0),
    337        scroll_spatial_id,
    338        root_pipeline_id,
    339        0.1,
    340        time,
    341        SpatialTreeItemKey::new(1, 3),
    342    );
    343 
    344    push_rotated_rect(
    345        builder,
    346        LayoutRect::from_origin_and_size(
    347            LayoutPoint::new(100.0, 600.0),
    348            LayoutSize::new(size_factor * 400.0, size_factor * 400.0),
    349        ),
    350        ColorF::new(1.0, 1.0, 0.0, 1.0),
    351        scroll_spatial_id,
    352        root_pipeline_id,
    353        time,
    354        time,
    355        SpatialTreeItemKey::new(1, 4),
    356    );
    357 
    358    push_rotated_rect(
    359        builder,
    360        LayoutRect::from_origin_and_size(
    361            LayoutPoint::new(700.0, 600.0),
    362            LayoutSize::new(size_factor * 400.0, size_factor * 400.0),
    363        ),
    364        ColorF::new(0.0, 1.0, 1.0, 1.0),
    365        scroll_spatial_id,
    366        root_pipeline_id,
    367        time,
    368        time,
    369        SpatialTreeItemKey::new(1, 5),
    370    );
    371 }
    372 
    373 #[derive(Debug, Copy, Clone)]
    374 enum Invalidations {
    375    Large,
    376    Small,
    377    Scrolling,
    378 }
    379 
    380 #[repr(C)]
    381 #[derive(Debug, Copy, Clone)]
    382 enum Sync {
    383    None = 0,
    384    Swap = 1,
    385    Commit = 2,
    386    Flush = 3,
    387    Query = 4,
    388 }
    389 
    390 fn main() {
    391    let args: Vec<String> = env::args().collect();
    392 
    393    if args.len() != 6 {
    394        println!("USAGE: compositor [native|none] [small|large|scroll] [none|swap|commit|flush|query] width height");
    395        process::exit(0);
    396    }
    397 
    398    let enable_compositor = match args[1].parse::<String>().unwrap().as_str() {
    399        "native" => true,
    400        "none" => false,
    401        _ => panic!("invalid compositor [native, none]"),
    402    };
    403 
    404    let inv_mode = match args[2].parse::<String>().unwrap().as_str() {
    405        "small" => Invalidations::Small,
    406        "large" => Invalidations::Large,
    407        "scroll" => Invalidations::Scrolling,
    408        _ => panic!("invalid invalidations [small, large, scroll]"),
    409    };
    410 
    411    let sync_mode = match args[3].parse::<String>().unwrap().as_str() {
    412        "none" => Sync::None,
    413        "swap" => Sync::Swap,
    414        "commit" => Sync::Commit,
    415        "flush" => Sync::Flush,
    416        "query" => Sync::Query,
    417        _ => panic!("invalid sync mode [none, swap, commit, flush, query]"),
    418    };
    419 
    420    let width = args[4].parse().unwrap();
    421    let height = args[5].parse().unwrap();
    422    let device_size = DeviceIntSize::new(width, height);
    423 
    424    // Load GL, construct WR and the native compositor interface.
    425    let window = compositor::create_window(
    426        device_size.width,
    427        device_size.height,
    428        enable_compositor,
    429        sync_mode as i32,
    430    );
    431    let debug_flags = DebugFlags::empty();
    432    let compositor_config = if enable_compositor {
    433        webrender::CompositorConfig::Native {
    434            compositor: Box::new(DirectCompositeInterface::new(window)),
    435        }
    436    } else {
    437        webrender::CompositorConfig::Draw {
    438            max_partial_present_rects: 0,
    439            draw_previous_partial_present_regions: false,
    440            partial_present: None,
    441        }
    442    };
    443    let opts = webrender::WebRenderOptions {
    444        clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
    445        debug_flags,
    446        compositor_config,
    447        surface_origin_is_top_left: false,
    448        ..webrender::WebRenderOptions::default()
    449    };
    450    let (tx, rx) = mpsc::channel();
    451    let notifier = Box::new(Notifier::new(tx));
    452    let gl = unsafe {
    453        gl::GlesFns::load_with(|symbol| {
    454            let symbol = CString::new(symbol).unwrap();
    455            let ptr = compositor::get_proc_address(symbol.as_ptr());
    456            ptr
    457        })
    458    };
    459    let (mut renderer, sender) =
    460        webrender::create_webrender_instance(gl.clone(), notifier, opts, None).unwrap();
    461    let mut api = sender.create_api();
    462    let document_id = api.add_document(device_size);
    463    let device_pixel_ratio = 1.0;
    464    let mut current_epoch = Epoch(0);
    465    let root_pipeline_id = PipelineId(0, 0);
    466    let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
    467    let mut time = 0.0;
    468    let scroll_id = ExternalScrollId(3, root_pipeline_id);
    469 
    470    // Kick off first transaction which will mean we get a notify below to build the DL and render.
    471    let mut txn = Transaction::new();
    472    txn.set_root_pipeline(root_pipeline_id);
    473 
    474    if let Invalidations::Scrolling = inv_mode {
    475        let mut root_builder = DisplayListBuilder::new(root_pipeline_id);
    476        root_builder.begin();
    477 
    478        build_display_list(
    479            &mut root_builder,
    480            scroll_id,
    481            root_pipeline_id,
    482            layout_size,
    483            1.0,
    484            inv_mode,
    485        );
    486 
    487        txn.set_display_list(current_epoch, root_builder.end());
    488    }
    489 
    490    txn.generate_frame(0, true, false, RenderReasons::empty());
    491    api.send_transaction(document_id, txn);
    492 
    493    // Tick the compositor (in this sample, we don't block on UI events)
    494    while compositor::tick(window) {
    495        // If there is a new frame ready to draw
    496        if let Ok(..) = rx.try_recv() {
    497            // Update and render. This will invoke the native compositor interface implemented above
    498            // as required.
    499            renderer.update();
    500            renderer.render(device_size, 0).unwrap();
    501            let _ = renderer.flush_pipeline_info();
    502 
    503            // Construct a simple display list that can be drawn and composited by DC.
    504            let mut txn = Transaction::new();
    505 
    506            match inv_mode {
    507                Invalidations::Small | Invalidations::Large => {
    508                    let mut root_builder = DisplayListBuilder::new(root_pipeline_id);
    509                    root_builder.begin();
    510 
    511                    build_display_list(
    512                        &mut root_builder,
    513                        scroll_id,
    514                        root_pipeline_id,
    515                        layout_size,
    516                        time,
    517                        inv_mode,
    518                    );
    519 
    520                    txn.set_display_list(current_epoch, root_builder.end());
    521                }
    522                Invalidations::Scrolling => {
    523                    let d = 0.5 - 0.5 * (2.0 * f32::consts::PI * 5.0 * time).cos();
    524                    txn.set_scroll_offsets(
    525                        scroll_id,
    526                        vec![SampledScrollOffset {
    527                            offset: LayoutVector2D::new(0.0, (d * 100.0).round()),
    528                            generation: APZScrollGeneration::default(),
    529                        }],
    530                    );
    531                }
    532            }
    533 
    534            txn.generate_frame(0, true, false, RenderReasons::empty());
    535            api.send_transaction(document_id, txn);
    536            current_epoch.0 += 1;
    537            time += 0.001;
    538            if time > 1.0 {
    539                time = 0.0;
    540            }
    541 
    542            // This does nothing when native compositor is enabled
    543            compositor::swap_buffers(window);
    544        }
    545    }
    546 
    547    renderer.deinit();
    548    compositor::destroy_window(window);
    549 }