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 CompositionPipeline;
6 use SendableFrameTree;
7 use compositor_thread::{CompositorProxy, CompositorReceiver};
8 use compositor_thread::{InitialCompositorState, Msg};
9 use euclid::{TypedPoint2D, TypedVector2D, TypedScale};
10 use gfx_traits::Epoch;
11 use gleam::gl;
12 use image::{DynamicImage, ImageFormat, RgbImage};
13 use ipc_channel::ipc::{self, IpcSharedMemory};
14 use libc::c_void;
15 use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId};
16 use net_traits::image::base::{Image, PixelFormat};
17 use nonzero::NonZero;
18 use profile_traits::time::{self, ProfilerCategory, profile};
19 use script_traits::{AnimationState, AnimationTickType, ConstellationMsg, LayoutControlMsg};
20 use script_traits::{MouseButton, MouseEventType, ScrollState, TouchEventType, TouchId};
21 use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
22 use script_traits::CompositorEvent::{MouseMoveEvent, MouseButtonEvent, TouchEvent};
23 use servo_config::opts;
24 use servo_geometry::DeviceIndependentPixel;
25 use std::collections::HashMap;
26 use std::fs::File;
27 use std::rc::Rc;
28 use std::sync::mpsc::Sender;
29 use std::time::{Duration, Instant};
30 use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
31 use style_traits::cursor::CursorKind;
32 use style_traits::viewport::ViewportConstraints;
33 use time::{precise_time_ns, precise_time_s};
34 use touch::{TouchHandler, TouchAction};
35 use webrender;
36 use webrender_api::{self, DeviceUintRect, DeviceUintSize, HitTestFlags, HitTestResult};
37 use webrender_api::{LayoutVector2D, ScrollEventPhase, ScrollLocation};
38 use windowing::{self, MouseWindowEvent, WebRenderDebugOption, WindowMethods};
39 
40 #[derive(Debug, PartialEq)]
41 enum UnableToComposite {
42     WindowUnprepared,
43     NotReadyToPaintImage(NotReadyToPaint),
44 }
45 
46 #[derive(Debug, PartialEq)]
47 enum NotReadyToPaint {
48     AnimationsActive,
49     JustNotifiedConstellation,
50     WaitingOnConstellation,
51 }
52 
53 // Default viewport constraints
54 const MAX_ZOOM: f32 = 8.0;
55 const MIN_ZOOM: f32 = 0.1;
56 
57 trait ConvertPipelineIdFromWebRender {
from_webrender(&self) -> PipelineId58     fn from_webrender(&self) -> PipelineId;
59 }
60 
61 impl ConvertPipelineIdFromWebRender for webrender_api::PipelineId {
from_webrender(&self) -> PipelineId62     fn from_webrender(&self) -> PipelineId {
63         PipelineId {
64             namespace_id: PipelineNamespaceId(self.0),
65             index: PipelineIndex(NonZero::new(self.1).expect("Webrender pipeline zero?")),
66         }
67     }
68 }
69 
70 /// Holds the state when running reftests that determines when it is
71 /// safe to save the output image.
72 #[derive(Clone, Copy, Debug, PartialEq)]
73 enum ReadyState {
74     Unknown,
75     WaitingForConstellationReply,
76     ReadyToSaveImage,
77 }
78 
79 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
80 struct FrameTreeId(u32);
81 
82 impl FrameTreeId {
next(&mut self)83     pub fn next(&mut self) {
84         self.0 += 1;
85     }
86 }
87 
88 /// One pixel in layer coordinate space.
89 ///
90 /// This unit corresponds to a "pixel" in layer coordinate space, which after scaling and
91 /// transformation becomes a device pixel.
92 #[derive(Clone, Copy, Debug)]
93 enum LayerPixel {}
94 
95 /// NB: Never block on the constellation, because sometimes the constellation blocks on us.
96 pub struct IOCompositor<Window: WindowMethods> {
97     /// The application window.
98     pub window: Rc<Window>,
99 
100     /// The port on which we receive messages.
101     port: CompositorReceiver,
102 
103     /// The root pipeline.
104     root_pipeline: Option<CompositionPipeline>,
105 
106     /// Tracks details about each active pipeline that the compositor knows about.
107     pipeline_details: HashMap<PipelineId, PipelineDetails>,
108 
109     /// The scene scale, to allow for zooming and high-resolution painting.
110     scale: TypedScale<f32, LayerPixel, DevicePixel>,
111 
112     /// The size of the rendering area.
113     frame_size: DeviceUintSize,
114 
115     /// The position and size of the window within the rendering area.
116     window_rect: DeviceUintRect,
117 
118     /// "Mobile-style" zoom that does not reflow the page.
119     viewport_zoom: PinchZoomFactor,
120 
121     /// Viewport zoom constraints provided by @viewport.
122     min_viewport_zoom: Option<PinchZoomFactor>,
123     max_viewport_zoom: Option<PinchZoomFactor>,
124 
125     /// "Desktop-style" zoom that resizes the viewport to fit the window.
126     page_zoom: TypedScale<f32, CSSPixel, DeviceIndependentPixel>,
127 
128     /// The device pixel ratio for this window.
129     scale_factor: TypedScale<f32, DeviceIndependentPixel, DevicePixel>,
130 
131     /// The type of composition to perform
132     composite_target: CompositeTarget,
133 
134     /// Tracks whether we should composite this frame.
135     composition_request: CompositionRequest,
136 
137     /// Tracks whether we are in the process of shutting down, or have shut down and should close
138     /// the compositor.
139     pub shutdown_state: ShutdownState,
140 
141     /// Tracks the last composite time.
142     last_composite_time: u64,
143 
144     /// Tracks whether the zoom action has happened recently.
145     zoom_action: bool,
146 
147     /// The time of the last zoom action has started.
148     zoom_time: f64,
149 
150     /// The current frame tree ID (used to reject old paint buffers)
151     frame_tree_id: FrameTreeId,
152 
153     /// The channel on which messages can be sent to the constellation.
154     constellation_chan: Sender<ConstellationMsg>,
155 
156     /// The channel on which messages can be sent to the time profiler.
157     time_profiler_chan: time::ProfilerChan,
158 
159     /// Touch input state machine
160     touch_handler: TouchHandler,
161 
162     /// Pending scroll/zoom events.
163     pending_scroll_zoom_events: Vec<ScrollZoomEvent>,
164 
165     /// Whether we're waiting on a recomposite after dispatching a scroll.
166     waiting_for_results_of_scroll: bool,
167 
168     /// Used by the logic that determines when it is safe to output an
169     /// image for the reftest framework.
170     ready_to_save_state: ReadyState,
171 
172     /// Whether a scroll is in progress; i.e. whether the user's fingers are down.
173     scroll_in_progress: bool,
174 
175     in_scroll_transaction: Option<Instant>,
176 
177     /// The webrender renderer.
178     webrender: webrender::Renderer,
179 
180     /// The active webrender document.
181     webrender_document: webrender_api::DocumentId,
182 
183     /// The webrender interface, if enabled.
184     webrender_api: webrender_api::RenderApi,
185 
186     /// GL functions interface (may be GL or GLES)
187     gl: Rc<gl::Gl>,
188 
189     /// Map of the pending paint metrics per layout thread.
190     /// The layout thread for each specific pipeline expects the compositor to
191     /// paint frames with specific given IDs (epoch). Once the compositor paints
192     /// these frames, it records the paint time for each of them and sends the
193     /// metric to the corresponding layout thread.
194     pending_paint_metrics: HashMap<PipelineId, Epoch>,
195 }
196 
197 #[derive(Clone, Copy)]
198 struct ScrollZoomEvent {
199     /// Change the pinch zoom level by this factor
200     magnification: f32,
201     /// Scroll by this offset, or to Start or End
202     scroll_location: ScrollLocation,
203     /// Apply changes to the frame at this location
204     cursor: TypedPoint2D<i32, DevicePixel>,
205     /// The scroll event phase.
206     phase: ScrollEventPhase,
207     /// The number of OS events that have been coalesced together into this one event.
208     event_count: u32,
209 }
210 
211 #[derive(Debug, PartialEq)]
212 enum CompositionRequest {
213     NoCompositingNecessary,
214     CompositeNow(CompositingReason),
215 }
216 
217 #[derive(Clone, Copy, Debug, PartialEq)]
218 pub enum ShutdownState {
219     NotShuttingDown,
220     ShuttingDown,
221     FinishedShuttingDown,
222 }
223 
224 struct PipelineDetails {
225     /// The pipeline associated with this PipelineDetails object.
226     pipeline: Option<CompositionPipeline>,
227 
228     /// Whether animations are running
229     animations_running: bool,
230 
231     /// Whether there are animation callbacks
232     animation_callbacks_running: bool,
233 
234     /// Whether this pipeline is visible
235     visible: bool,
236 }
237 
238 impl PipelineDetails {
new() -> PipelineDetails239     fn new() -> PipelineDetails {
240         PipelineDetails {
241             pipeline: None,
242             animations_running: false,
243             animation_callbacks_running: false,
244             visible: true,
245         }
246     }
247 }
248 
249 #[derive(Clone, Copy, Debug, PartialEq)]
250 enum CompositeTarget {
251     /// Normal composition to a window
252     Window,
253 
254     /// Compose as normal, but also return a PNG of the composed output
255     WindowAndPng,
256 
257     /// Compose to a PNG, write it to disk, and then exit the browser (used for reftests)
258     PngFile
259 }
260 
261 struct RenderTargetInfo {
262     framebuffer_ids: Vec<gl::GLuint>,
263     renderbuffer_ids: Vec<gl::GLuint>,
264     texture_ids: Vec<gl::GLuint>,
265 }
266 
267 impl RenderTargetInfo {
empty() -> RenderTargetInfo268     fn empty() -> RenderTargetInfo {
269         RenderTargetInfo {
270             framebuffer_ids: Vec::new(),
271             renderbuffer_ids: Vec::new(),
272             texture_ids: Vec::new(),
273         }
274     }
275 }
276 
initialize_png(gl: &gl::Gl, width: usize, height: usize) -> RenderTargetInfo277 fn initialize_png(gl: &gl::Gl, width: usize, height: usize) -> RenderTargetInfo {
278     let framebuffer_ids = gl.gen_framebuffers(1);
279     gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]);
280 
281     let texture_ids = gl.gen_textures(1);
282     gl.bind_texture(gl::TEXTURE_2D, texture_ids[0]);
283 
284     gl.tex_image_2d(gl::TEXTURE_2D, 0, gl::RGB as gl::GLint, width as gl::GLsizei,
285                     height as gl::GLsizei, 0, gl::RGB, gl::UNSIGNED_BYTE, None);
286     gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as gl::GLint);
287     gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as gl::GLint);
288 
289     gl.framebuffer_texture_2d(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D,
290                               texture_ids[0], 0);
291 
292     gl.bind_texture(gl::TEXTURE_2D, 0);
293 
294     let renderbuffer_ids = gl.gen_renderbuffers(1);
295     let depth_rb = renderbuffer_ids[0];
296     gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
297     gl.renderbuffer_storage(gl::RENDERBUFFER,
298                             gl::DEPTH_COMPONENT24,
299                             width as gl::GLsizei,
300                             height as gl::GLsizei);
301     gl.framebuffer_renderbuffer(gl::FRAMEBUFFER,
302                                 gl::DEPTH_ATTACHMENT,
303                                 gl::RENDERBUFFER,
304                                 depth_rb);
305 
306     RenderTargetInfo {
307         framebuffer_ids: framebuffer_ids,
308         renderbuffer_ids: renderbuffer_ids,
309         texture_ids: texture_ids,
310     }
311 }
312 
313 #[derive(Clone)]
314 pub struct RenderNotifier {
315     compositor_proxy: CompositorProxy,
316 }
317 
318 impl RenderNotifier {
new(compositor_proxy: CompositorProxy) -> RenderNotifier319     pub fn new(compositor_proxy: CompositorProxy) -> RenderNotifier {
320         RenderNotifier {
321             compositor_proxy: compositor_proxy,
322         }
323     }
324 }
325 
326 impl webrender_api::RenderNotifier for RenderNotifier {
clone(&self) -> Box<webrender_api::RenderNotifier>327     fn clone(&self) -> Box<webrender_api::RenderNotifier> {
328         Box::new(RenderNotifier::new(self.compositor_proxy.clone()))
329     }
330 
wake_up(&self)331     fn wake_up(&self) {
332         self.compositor_proxy.recomposite(CompositingReason::NewWebRenderFrame);
333     }
334 
new_document_ready( &self, _document_id: webrender_api::DocumentId, scrolled: bool, composite_needed: bool, )335     fn new_document_ready(
336         &self,
337         _document_id: webrender_api::DocumentId,
338         scrolled: bool,
339         composite_needed: bool,
340     ) {
341         if scrolled {
342             self.compositor_proxy.send(Msg::NewScrollFrameReady(composite_needed));
343         } else {
344             self.wake_up();
345         }
346     }
347 }
348 
349 impl<Window: WindowMethods> IOCompositor<Window> {
new(window: Rc<Window>, state: InitialCompositorState) -> IOCompositor<Window>350     fn new(window: Rc<Window>, state: InitialCompositorState)
351            -> IOCompositor<Window> {
352         let frame_size = window.framebuffer_size();
353         let window_rect = window.window_rect();
354         let scale_factor = window.hidpi_factor();
355         let composite_target = match opts::get().output_file {
356             Some(_) => CompositeTarget::PngFile,
357             None => CompositeTarget::Window
358         };
359 
360         IOCompositor {
361             gl: window.gl(),
362             window: window,
363             port: state.receiver,
364             root_pipeline: None,
365             pipeline_details: HashMap::new(),
366             frame_size: frame_size,
367             window_rect: window_rect,
368             scale: TypedScale::new(1.0),
369             scale_factor: scale_factor,
370             composition_request: CompositionRequest::NoCompositingNecessary,
371             touch_handler: TouchHandler::new(),
372             pending_scroll_zoom_events: Vec::new(),
373             waiting_for_results_of_scroll: false,
374             composite_target: composite_target,
375             shutdown_state: ShutdownState::NotShuttingDown,
376             page_zoom: TypedScale::new(1.0),
377             viewport_zoom: PinchZoomFactor::new(1.0),
378             min_viewport_zoom: None,
379             max_viewport_zoom: None,
380             zoom_action: false,
381             zoom_time: 0f64,
382             frame_tree_id: FrameTreeId(0),
383             constellation_chan: state.constellation_chan,
384             time_profiler_chan: state.time_profiler_chan,
385             last_composite_time: 0,
386             ready_to_save_state: ReadyState::Unknown,
387             scroll_in_progress: false,
388             in_scroll_transaction: None,
389             webrender: state.webrender,
390             webrender_document: state.webrender_document,
391             webrender_api: state.webrender_api,
392             pending_paint_metrics: HashMap::new(),
393         }
394     }
395 
create(window: Rc<Window>, state: InitialCompositorState) -> IOCompositor<Window>396     pub fn create(window: Rc<Window>, state: InitialCompositorState) -> IOCompositor<Window> {
397         let mut compositor = IOCompositor::new(window, state);
398 
399         // Set the size of the root layer.
400         compositor.update_zoom_transform();
401 
402         // Tell the constellation about the initial window size.
403         compositor.send_window_size(WindowSizeType::Initial);
404 
405         compositor
406     }
407 
deinit(self)408     pub fn deinit(self) {
409         self.webrender.deinit();
410     }
411 
maybe_start_shutting_down(&mut self)412     pub fn maybe_start_shutting_down(&mut self) {
413         if self.shutdown_state == ShutdownState::NotShuttingDown {
414             debug!("Shutting down the constellation for WindowEvent::Quit");
415             self.start_shutting_down();
416         }
417     }
418 
start_shutting_down(&mut self)419     fn start_shutting_down(&mut self) {
420         debug!("Compositor sending Exit message to Constellation");
421         if let Err(e) = self.constellation_chan.send(ConstellationMsg::Exit) {
422             warn!("Sending exit message to constellation failed ({}).", e);
423         }
424 
425         self.shutdown_state = ShutdownState::ShuttingDown;
426     }
427 
finish_shutting_down(&mut self)428     fn finish_shutting_down(&mut self) {
429         debug!("Compositor received message that constellation shutdown is complete");
430 
431         // Drain compositor port, sometimes messages contain channels that are blocking
432         // another thread from finishing (i.e. SetFrameTree).
433         while self.port.try_recv_compositor_msg().is_some() {}
434 
435         // Tell the profiler, memory profiler, and scrolling timer to shut down.
436         if let Ok((sender, receiver)) = ipc::channel() {
437             self.time_profiler_chan.send(time::ProfilerMsg::Exit(sender));
438             let _ = receiver.recv();
439         }
440 
441         self.shutdown_state = ShutdownState::FinishedShuttingDown;
442     }
443 
handle_browser_message(&mut self, msg: Msg) -> bool444     fn handle_browser_message(&mut self, msg: Msg) -> bool {
445         match (msg, self.shutdown_state) {
446             (_, ShutdownState::FinishedShuttingDown) => {
447                 error!("compositor shouldn't be handling messages after shutting down");
448                 return false
449             }
450 
451             (Msg::Exit, _) => {
452                 self.start_shutting_down();
453             }
454 
455             (Msg::ShutdownComplete, _) => {
456                 self.finish_shutting_down();
457                 return false;
458             }
459 
460             (Msg::ChangeRunningAnimationsState(pipeline_id, animation_state),
461              ShutdownState::NotShuttingDown) => {
462                 self.change_running_animations_state(pipeline_id, animation_state);
463             }
464 
465             (Msg::SetFrameTree(frame_tree),
466              ShutdownState::NotShuttingDown) => {
467                 self.set_frame_tree(&frame_tree);
468                 self.send_viewport_rects();
469             }
470 
471             (Msg::Recomposite(reason), ShutdownState::NotShuttingDown) => {
472                 self.composition_request = CompositionRequest::CompositeNow(reason)
473             }
474 
475 
476             (Msg::TouchEventProcessed(result), ShutdownState::NotShuttingDown) => {
477                 self.touch_handler.on_event_processed(result);
478             }
479 
480             (Msg::CreatePng(reply), ShutdownState::NotShuttingDown) => {
481                 let res = self.composite_specific_target(CompositeTarget::WindowAndPng);
482                 if let Err(ref e) = res {
483                     info!("Error retrieving PNG: {:?}", e);
484                 }
485                 let img = res.unwrap_or(None);
486                 if let Err(e) = reply.send(img) {
487                     warn!("Sending reply to create png failed ({}).", e);
488                 }
489             }
490 
491             (Msg::ViewportConstrained(pipeline_id, constraints),
492              ShutdownState::NotShuttingDown) => {
493                 self.constrain_viewport(pipeline_id, constraints);
494             }
495 
496             (Msg::IsReadyToSaveImageReply(is_ready), ShutdownState::NotShuttingDown) => {
497                 assert_eq!(self.ready_to_save_state, ReadyState::WaitingForConstellationReply);
498                 if is_ready {
499                     self.ready_to_save_state = ReadyState::ReadyToSaveImage;
500                     if opts::get().is_running_problem_test {
501                         println!("ready to save image!");
502                     }
503                 } else {
504                     self.ready_to_save_state = ReadyState::Unknown;
505                     if opts::get().is_running_problem_test {
506                         println!("resetting ready_to_save_state!");
507                     }
508                 }
509                 self.composite_if_necessary(CompositingReason::Headless);
510             }
511 
512             (Msg::PipelineVisibilityChanged(pipeline_id, visible), ShutdownState::NotShuttingDown) => {
513                 self.pipeline_details(pipeline_id).visible = visible;
514                 if visible {
515                     self.process_animations();
516                 }
517             }
518 
519             (Msg::PipelineExited(pipeline_id, sender), _) => {
520                 debug!("Compositor got pipeline exited: {:?}", pipeline_id);
521                 self.remove_pipeline_root_layer(pipeline_id);
522                 let _ = sender.send(());
523             }
524 
525             (Msg::NewScrollFrameReady(recomposite_needed), ShutdownState::NotShuttingDown) => {
526                 self.waiting_for_results_of_scroll = false;
527                 if recomposite_needed {
528                     self.composition_request = CompositionRequest::CompositeNow(
529                         CompositingReason::NewWebRenderScrollFrame);
530                 }
531             }
532 
533             (Msg::Dispatch(func), ShutdownState::NotShuttingDown) => {
534                 // The functions sent here right now are really dumb, so they can't panic.
535                 // But if we start running more complex code here, we should really catch panic here.
536                 func();
537             }
538 
539             (Msg::LoadComplete(_), ShutdownState::NotShuttingDown) => {
540                 // If we're painting in headless mode, schedule a recomposite.
541                 if opts::get().output_file.is_some() || opts::get().exit_after_load {
542                     self.composite_if_necessary(CompositingReason::Headless);
543                 }
544             },
545 
546             (Msg::PendingPaintMetric(pipeline_id, epoch), _) => {
547                 self.pending_paint_metrics.insert(pipeline_id, epoch);
548             }
549 
550             // When we are shutting_down, we need to avoid performing operations
551             // such as Paint that may crash because we have begun tearing down
552             // the rest of our resources.
553             (_, ShutdownState::ShuttingDown) => {}
554         }
555 
556         true
557     }
558 
559     /// Sets or unsets the animations-running flag for the given pipeline, and schedules a
560     /// recomposite if necessary.
change_running_animations_state(&mut self, pipeline_id: PipelineId, animation_state: AnimationState)561     fn change_running_animations_state(&mut self,
562                                        pipeline_id: PipelineId,
563                                        animation_state: AnimationState) {
564         match animation_state {
565             AnimationState::AnimationsPresent => {
566                 let visible = self.pipeline_details(pipeline_id).visible;
567                 self.pipeline_details(pipeline_id).animations_running = true;
568                 if visible {
569                     self.composite_if_necessary(CompositingReason::Animation);
570                 }
571             }
572             AnimationState::AnimationCallbacksPresent => {
573                 let visible = self.pipeline_details(pipeline_id).visible;
574                 self.pipeline_details(pipeline_id).animation_callbacks_running = true;
575                 if visible {
576                     self.tick_animations_for_pipeline(pipeline_id);
577                 }
578             }
579             AnimationState::NoAnimationsPresent => {
580                 self.pipeline_details(pipeline_id).animations_running = false;
581             }
582             AnimationState::NoAnimationCallbacksPresent => {
583                 self.pipeline_details(pipeline_id).animation_callbacks_running = false;
584             }
585         }
586     }
587 
pipeline_details(&mut self, pipeline_id: PipelineId) -> &mut PipelineDetails588     fn pipeline_details(&mut self, pipeline_id: PipelineId) -> &mut PipelineDetails {
589         if !self.pipeline_details.contains_key(&pipeline_id) {
590             self.pipeline_details.insert(pipeline_id, PipelineDetails::new());
591         }
592         self.pipeline_details.get_mut(&pipeline_id).expect("Insert then get failed!")
593     }
594 
pipeline(&self, pipeline_id: PipelineId) -> Option<&CompositionPipeline>595     pub fn pipeline(&self, pipeline_id: PipelineId) -> Option<&CompositionPipeline> {
596         match self.pipeline_details.get(&pipeline_id) {
597             Some(ref details) => details.pipeline.as_ref(),
598             None => {
599                 warn!("Compositor layer has an unknown pipeline ({:?}).", pipeline_id);
600                 None
601             }
602         }
603     }
604 
set_frame_tree(&mut self, frame_tree: &SendableFrameTree)605     fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
606         debug!("Setting the frame tree for pipeline {}", frame_tree.pipeline.id);
607 
608         self.root_pipeline = Some(frame_tree.pipeline.clone());
609 
610         let pipeline_id = frame_tree.pipeline.id.to_webrender();
611         let mut txn = webrender_api::Transaction::new();
612         txn.set_root_pipeline(pipeline_id);
613         txn.generate_frame();
614         self.webrender_api.send_transaction(self.webrender_document, txn);
615 
616         self.create_pipeline_details_for_frame_tree(&frame_tree);
617 
618         self.send_window_size(WindowSizeType::Initial);
619 
620         self.frame_tree_id.next();
621     }
622 
create_pipeline_details_for_frame_tree(&mut self, frame_tree: &SendableFrameTree)623     fn create_pipeline_details_for_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
624         self.pipeline_details(frame_tree.pipeline.id).pipeline = Some(frame_tree.pipeline.clone());
625 
626         for kid in &frame_tree.children {
627             self.create_pipeline_details_for_frame_tree(kid);
628         }
629     }
630 
remove_pipeline_root_layer(&mut self, pipeline_id: PipelineId)631     fn remove_pipeline_root_layer(&mut self, pipeline_id: PipelineId) {
632         self.pipeline_details.remove(&pipeline_id);
633     }
634 
send_window_size(&self, size_type: WindowSizeType)635     fn send_window_size(&self, size_type: WindowSizeType) {
636         let dppx = self.page_zoom * self.hidpi_factor();
637 
638         self.webrender_api.set_window_parameters(self.webrender_document,
639                                                  self.frame_size,
640                                                  self.window_rect,
641                                                  self.hidpi_factor().get());
642 
643         let initial_viewport = self.window_rect.size.to_f32() / dppx;
644 
645         let data = WindowSizeData {
646             device_pixel_ratio: dppx,
647             initial_viewport: initial_viewport,
648         };
649         let top_level_browsing_context_id = self.root_pipeline.as_ref().map(|pipeline| {
650             pipeline.top_level_browsing_context_id
651         });
652         let msg = ConstellationMsg::WindowSize(top_level_browsing_context_id, data, size_type);
653 
654         if let Err(e) = self.constellation_chan.send(msg) {
655             warn!("Sending window resize to constellation failed ({}).", e);
656         }
657     }
658 
on_resize_window_event(&mut self)659     pub fn on_resize_window_event(&mut self) {
660         debug!("compositor resize requested");
661 
662         // A size change could also mean a resolution change.
663         let new_scale_factor = self.window.hidpi_factor();
664         if self.scale_factor != new_scale_factor {
665             self.scale_factor = new_scale_factor;
666             self.update_zoom_transform();
667         }
668 
669         let new_window_rect = self.window.window_rect();
670         let new_frame_size = self.window.framebuffer_size();
671 
672         if self.window_rect == new_window_rect &&
673            self.frame_size == new_frame_size {
674             return;
675         }
676 
677         self.frame_size = new_frame_size;
678         self.window_rect = new_window_rect;
679 
680         self.send_window_size(WindowSizeType::Resize);
681     }
682 
on_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent)683     pub fn on_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent) {
684         if opts::get().convert_mouse_to_touch {
685             match mouse_window_event {
686                 MouseWindowEvent::Click(_, _) => {}
687                 MouseWindowEvent::MouseDown(_, p) => self.on_touch_down(TouchId(0), p),
688                 MouseWindowEvent::MouseUp(_, p) => self.on_touch_up(TouchId(0), p),
689             }
690             return
691         }
692 
693         self.dispatch_mouse_window_event_class(mouse_window_event);
694     }
695 
dispatch_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent)696     fn dispatch_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent) {
697         let point = match mouse_window_event {
698             MouseWindowEvent::Click(_, p) => p,
699             MouseWindowEvent::MouseDown(_, p) => p,
700             MouseWindowEvent::MouseUp(_, p) => p,
701         };
702 
703         let results = self.hit_test_at_point(point);
704         let result = match results.items.first() {
705             Some(result) => result,
706             None => return,
707         };
708 
709         let (button, event_type) = match mouse_window_event {
710             MouseWindowEvent::Click(button, _) => (button, MouseEventType::Click),
711             MouseWindowEvent::MouseDown(button, _) => (button, MouseEventType::MouseDown),
712             MouseWindowEvent::MouseUp(button, _) => (button, MouseEventType::MouseUp),
713         };
714 
715         let event_to_send = MouseButtonEvent(
716             event_type,
717             button,
718             result.point_in_viewport.to_untyped(),
719             Some(UntrustedNodeAddress(result.tag.0 as *const c_void)),
720             Some(result.point_relative_to_item.to_untyped()),
721         );
722 
723         let pipeline_id = PipelineId::from_webrender(result.pipeline);
724         let msg = ConstellationMsg::ForwardEvent(pipeline_id, event_to_send);
725         if let Err(e) = self.constellation_chan.send(msg) {
726             warn!("Sending event to constellation failed ({}).", e);
727         }
728     }
729 
hit_test_at_point(&self, point: TypedPoint2D<f32, DevicePixel>) -> HitTestResult730     fn hit_test_at_point(&self, point: TypedPoint2D<f32, DevicePixel>) -> HitTestResult {
731         let dppx = self.page_zoom * self.hidpi_factor();
732         let scaled_point = (point / dppx).to_untyped();
733 
734         let world_cursor = webrender_api::WorldPoint::from_untyped(&scaled_point);
735         self.webrender_api.hit_test(
736             self.webrender_document,
737             None,
738             world_cursor,
739             HitTestFlags::empty()
740         )
741 
742     }
743 
on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<f32, DevicePixel>)744     pub fn on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<f32, DevicePixel>) {
745         if opts::get().convert_mouse_to_touch {
746             self.on_touch_move(TouchId(0), cursor);
747             return
748         }
749 
750         self.dispatch_mouse_window_move_event_class(cursor);
751     }
752 
dispatch_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<f32, DevicePixel>)753     fn dispatch_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<f32, DevicePixel>) {
754         let root_pipeline_id = match self.get_root_pipeline_id() {
755             Some(root_pipeline_id) => root_pipeline_id,
756             None => return,
757         };
758         if self.pipeline(root_pipeline_id).is_none() {
759             return;
760         }
761 
762         let results = self.hit_test_at_point(cursor);
763         if let Some(item) = results.items.first() {
764             let node_address = Some(UntrustedNodeAddress(item.tag.0 as *const c_void));
765             let event = MouseMoveEvent(Some(item.point_in_viewport.to_untyped()), node_address);
766             let pipeline_id = PipelineId::from_webrender(item.pipeline);
767             let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
768             if let Err(e) = self.constellation_chan.send(msg) {
769                 warn!("Sending event to constellation failed ({}).", e);
770             }
771 
772             if let Some(cursor) =  CursorKind::from_u8(item.tag.1 as _).ok() {
773                 let msg = ConstellationMsg::SetCursor(cursor);
774                 if let Err(e) = self.constellation_chan.send(msg) {
775                     warn!("Sending event to constellation failed ({}).", e);
776                 }
777             }
778         }
779     }
780 
send_touch_event( &self, event_type: TouchEventType, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>)781     fn send_touch_event(
782         &self,
783         event_type: TouchEventType,
784         identifier: TouchId,
785         point: TypedPoint2D<f32, DevicePixel>)
786     {
787         let results = self.hit_test_at_point(point);
788         if let Some(item) = results.items.first() {
789             let event = TouchEvent(
790                 event_type,
791                 identifier,
792                 item.point_in_viewport.to_untyped(),
793                 Some(UntrustedNodeAddress(item.tag.0 as *const c_void)),
794             );
795             let pipeline_id = PipelineId::from_webrender(item.pipeline);
796             let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
797             if let Err(e) = self.constellation_chan.send(msg) {
798                 warn!("Sending event to constellation failed ({}).", e);
799             }
800         }
801     }
802 
on_touch_event(&mut self, event_type: TouchEventType, identifier: TouchId, location: TypedPoint2D<f32, DevicePixel>)803     pub fn on_touch_event(&mut self,
804                           event_type: TouchEventType,
805                           identifier: TouchId,
806                           location: TypedPoint2D<f32, DevicePixel>) {
807         match event_type {
808             TouchEventType::Down => self.on_touch_down(identifier, location),
809             TouchEventType::Move => self.on_touch_move(identifier, location),
810             TouchEventType::Up => self.on_touch_up(identifier, location),
811             TouchEventType::Cancel => self.on_touch_cancel(identifier, location),
812         }
813     }
814 
on_touch_down(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>)815     fn on_touch_down(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
816         self.touch_handler.on_touch_down(identifier, point);
817         self.send_touch_event(TouchEventType::Down, identifier, point);
818     }
819 
on_touch_move(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>)820     fn on_touch_move(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
821         match self.touch_handler.on_touch_move(identifier, point) {
822             TouchAction::Scroll(delta) => {
823                 match point.cast() {
824                     Some(point) => self.on_scroll_window_event(
825                         ScrollLocation::Delta(
826                             LayoutVector2D::from_untyped(&delta.to_untyped())
827                         ),
828                         point
829                     ),
830                     None => error!("Point cast failed."),
831                 }
832             }
833             TouchAction::Zoom(magnification, scroll_delta) => {
834                 let cursor = TypedPoint2D::new(-1, -1);  // Make sure this hits the base layer.
835                 self.pending_scroll_zoom_events.push(ScrollZoomEvent {
836                     magnification: magnification,
837                     scroll_location: ScrollLocation::Delta(webrender_api::LayoutVector2D::from_untyped(
838                                                            &scroll_delta.to_untyped())),
839                     cursor: cursor,
840                     phase: ScrollEventPhase::Move(true),
841                     event_count: 1,
842                 });
843             }
844             TouchAction::DispatchEvent => {
845                 self.send_touch_event(TouchEventType::Move, identifier, point);
846             }
847             _ => {}
848         }
849     }
850 
on_touch_up(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>)851     fn on_touch_up(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
852         self.send_touch_event(TouchEventType::Up, identifier, point);
853 
854         if let TouchAction::Click = self.touch_handler.on_touch_up(identifier, point) {
855             self.simulate_mouse_click(point);
856         }
857     }
858 
on_touch_cancel(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>)859     fn on_touch_cancel(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
860         // Send the event to script.
861         self.touch_handler.on_touch_cancel(identifier, point);
862         self.send_touch_event(TouchEventType::Cancel, identifier, point);
863     }
864 
865     /// <http://w3c.github.io/touch-events/#mouse-events>
simulate_mouse_click(&mut self, p: TypedPoint2D<f32, DevicePixel>)866     fn simulate_mouse_click(&mut self, p: TypedPoint2D<f32, DevicePixel>) {
867         let button = MouseButton::Left;
868         self.dispatch_mouse_window_move_event_class(p);
869         self.dispatch_mouse_window_event_class(MouseWindowEvent::MouseDown(button, p));
870         self.dispatch_mouse_window_event_class(MouseWindowEvent::MouseUp(button, p));
871         self.dispatch_mouse_window_event_class(MouseWindowEvent::Click(button, p));
872     }
873 
on_scroll_event(&mut self, delta: ScrollLocation, cursor: TypedPoint2D<i32, DevicePixel>, phase: TouchEventType)874     pub fn on_scroll_event(&mut self,
875                            delta: ScrollLocation,
876                            cursor: TypedPoint2D<i32, DevicePixel>,
877                            phase: TouchEventType) {
878         match phase {
879             TouchEventType::Move => self.on_scroll_window_event(delta, cursor),
880             TouchEventType::Up | TouchEventType::Cancel => {
881                 self.on_scroll_end_window_event(delta, cursor);
882             }
883             TouchEventType::Down => {
884                 self.on_scroll_start_window_event(delta, cursor);
885             }
886         }
887     }
888 
on_scroll_window_event(&mut self, scroll_location: ScrollLocation, cursor: TypedPoint2D<i32, DevicePixel>)889     fn on_scroll_window_event(&mut self,
890                               scroll_location: ScrollLocation,
891                               cursor: TypedPoint2D<i32, DevicePixel>) {
892         let event_phase = match (self.scroll_in_progress, self.in_scroll_transaction) {
893             (false, None) => ScrollEventPhase::Start,
894             (false, Some(last_scroll)) if last_scroll.elapsed() > Duration::from_millis(80) =>
895                 ScrollEventPhase::Start,
896             (_, _) => ScrollEventPhase::Move(self.scroll_in_progress),
897         };
898         self.in_scroll_transaction = Some(Instant::now());
899         self.pending_scroll_zoom_events.push(ScrollZoomEvent {
900             magnification: 1.0,
901             scroll_location: scroll_location,
902             cursor: cursor,
903             phase: event_phase,
904             event_count: 1,
905         });
906     }
907 
on_scroll_start_window_event(&mut self, scroll_location: ScrollLocation, cursor: TypedPoint2D<i32, DevicePixel>)908     fn on_scroll_start_window_event(&mut self,
909                                     scroll_location: ScrollLocation,
910                                     cursor: TypedPoint2D<i32, DevicePixel>) {
911         self.scroll_in_progress = true;
912         self.pending_scroll_zoom_events.push(ScrollZoomEvent {
913             magnification: 1.0,
914             scroll_location: scroll_location,
915             cursor: cursor,
916             phase: ScrollEventPhase::Start,
917             event_count: 1,
918         });
919     }
920 
on_scroll_end_window_event(&mut self, scroll_location: ScrollLocation, cursor: TypedPoint2D<i32, DevicePixel>)921     fn on_scroll_end_window_event(&mut self,
922                                   scroll_location: ScrollLocation,
923                                   cursor: TypedPoint2D<i32, DevicePixel>) {
924         self.scroll_in_progress = false;
925         self.pending_scroll_zoom_events.push(ScrollZoomEvent {
926             magnification: 1.0,
927             scroll_location: scroll_location,
928             cursor: cursor,
929             phase: ScrollEventPhase::End,
930             event_count: 1,
931         });
932     }
933 
process_pending_scroll_events(&mut self)934     fn process_pending_scroll_events(&mut self) {
935         let had_events = self.pending_scroll_zoom_events.len() > 0;
936 
937         // Batch up all scroll events into one, or else we'll do way too much painting.
938         let mut last_combined_event: Option<ScrollZoomEvent> = None;
939         for scroll_event in self.pending_scroll_zoom_events.drain(..) {
940             let this_cursor = scroll_event.cursor;
941 
942             let this_delta = match scroll_event.scroll_location {
943                 ScrollLocation::Delta(delta) => delta,
944                 ScrollLocation::Start | ScrollLocation::End => {
945                     // If this is an event which is scrolling to the start or end of the page,
946                     // disregard other pending events and exit the loop.
947                     last_combined_event = Some(scroll_event);
948                     break;
949                 }
950             };
951 
952             if let Some(combined_event) = last_combined_event {
953                 if combined_event.phase != scroll_event.phase {
954                     let combined_delta = match combined_event.scroll_location {
955                         ScrollLocation::Delta(delta) => delta,
956                         ScrollLocation::Start | ScrollLocation::End => {
957                             // If this is an event which is scrolling to the start or end of the page,
958                             // disregard other pending events and exit the loop.
959                             last_combined_event = Some(scroll_event);
960                             break;
961                         }
962                     };
963                     // TODO: units don't match!
964                     let delta = combined_delta / self.scale.get();
965 
966                     let cursor =
967                         (combined_event.cursor.to_f32() / self.scale).to_untyped();
968                     let location = webrender_api::ScrollLocation::Delta(delta);
969                     let cursor = webrender_api::WorldPoint::from_untyped(&cursor);
970                     let mut txn = webrender_api::Transaction::new();
971                     txn.scroll(location, cursor, combined_event.phase);
972                     self.webrender_api.send_transaction(self.webrender_document, txn);
973                     last_combined_event = None
974                 }
975             }
976 
977             match (&mut last_combined_event, scroll_event.phase) {
978                 (last_combined_event @ &mut None, _) => {
979                     *last_combined_event = Some(ScrollZoomEvent {
980                         magnification: scroll_event.magnification,
981                         scroll_location: ScrollLocation::Delta(webrender_api::LayoutVector2D::from_untyped(
982                                                                &this_delta.to_untyped())),
983                         cursor: this_cursor,
984                         phase: scroll_event.phase,
985                         event_count: 1,
986                     })
987                 }
988                 (&mut Some(ref mut last_combined_event),
989                  ScrollEventPhase::Move(false)) => {
990                     // Mac OS X sometimes delivers scroll events out of vsync during a
991                     // fling. This causes events to get bunched up occasionally, causing
992                     // nasty-looking "pops". To mitigate this, during a fling we average
993                     // deltas instead of summing them.
994                     if let ScrollLocation::Delta(delta) = last_combined_event.scroll_location {
995                         let old_event_count =
996                             TypedScale::new(last_combined_event.event_count as f32);
997                         last_combined_event.event_count += 1;
998                         let new_event_count =
999                             TypedScale::new(last_combined_event.event_count as f32);
1000                         last_combined_event.scroll_location = ScrollLocation::Delta(
1001                             (delta * old_event_count + this_delta) /
1002                             new_event_count);
1003                     }
1004                 }
1005                 (&mut Some(ref mut last_combined_event), _) => {
1006                     if let ScrollLocation::Delta(delta) = last_combined_event.scroll_location {
1007                         last_combined_event.scroll_location = ScrollLocation::Delta(delta + this_delta);
1008                         last_combined_event.event_count += 1
1009                     }
1010                 }
1011             }
1012         }
1013 
1014         if let Some(combined_event) = last_combined_event {
1015             let scroll_location = match combined_event.scroll_location {
1016                 ScrollLocation::Delta(delta) => {
1017                     let scaled_delta = (TypedVector2D::from_untyped(&delta.to_untyped()) / self.scale)
1018                                        .to_untyped();
1019                     let calculated_delta = webrender_api::LayoutVector2D::from_untyped(&scaled_delta);
1020                                            ScrollLocation::Delta(calculated_delta)
1021                 },
1022                 // Leave ScrollLocation unchanged if it is Start or End location.
1023                 sl @ ScrollLocation::Start | sl @ ScrollLocation::End => sl,
1024             };
1025             let cursor = (combined_event.cursor.to_f32() / self.scale).to_untyped();
1026             let cursor = webrender_api::WorldPoint::from_untyped(&cursor);
1027             let mut txn = webrender_api::Transaction::new();
1028             txn.scroll(scroll_location, cursor, combined_event.phase);
1029             self.webrender_api.send_transaction(self.webrender_document, txn);
1030             self.waiting_for_results_of_scroll = true
1031         }
1032 
1033         if had_events {
1034             self.send_viewport_rects();
1035         }
1036     }
1037 
1038     /// If there are any animations running, dispatches appropriate messages to the constellation.
process_animations(&mut self)1039     fn process_animations(&mut self) {
1040         let mut pipeline_ids = vec![];
1041         for (pipeline_id, pipeline_details) in &self.pipeline_details {
1042             if (pipeline_details.animations_running ||
1043                 pipeline_details.animation_callbacks_running) &&
1044                pipeline_details.visible {
1045                    pipeline_ids.push(*pipeline_id);
1046             }
1047         }
1048         let animation_state = if pipeline_ids.is_empty() {
1049             windowing::AnimationState::Idle
1050         } else {
1051             windowing::AnimationState::Animating
1052         };
1053         self.window.set_animation_state(animation_state);
1054         for pipeline_id in &pipeline_ids {
1055             self.tick_animations_for_pipeline(*pipeline_id)
1056         }
1057     }
1058 
tick_animations_for_pipeline(&mut self, pipeline_id: PipelineId)1059     fn tick_animations_for_pipeline(&mut self, pipeline_id: PipelineId) {
1060         let animation_callbacks_running = self.pipeline_details(pipeline_id).animation_callbacks_running;
1061         if animation_callbacks_running {
1062             let msg = ConstellationMsg::TickAnimation(pipeline_id, AnimationTickType::Script);
1063             if let Err(e) = self.constellation_chan.send(msg) {
1064                 warn!("Sending tick to constellation failed ({}).", e);
1065             }
1066         }
1067 
1068         // We may need to tick animations in layout. (See #12749.)
1069         let animations_running = self.pipeline_details(pipeline_id).animations_running;
1070         if animations_running {
1071             let msg = ConstellationMsg::TickAnimation(pipeline_id, AnimationTickType::Layout);
1072             if let Err(e) = self.constellation_chan.send(msg) {
1073                 warn!("Sending tick to constellation failed ({}).", e);
1074             }
1075         }
1076     }
1077 
constrain_viewport(&mut self, pipeline_id: PipelineId, constraints: ViewportConstraints)1078     fn constrain_viewport(&mut self, pipeline_id: PipelineId, constraints: ViewportConstraints) {
1079         let is_root = self.root_pipeline.as_ref().map_or(false, |root_pipeline| {
1080             root_pipeline.id == pipeline_id
1081         });
1082 
1083         if is_root {
1084             self.viewport_zoom = constraints.initial_zoom;
1085             self.min_viewport_zoom = constraints.min_zoom;
1086             self.max_viewport_zoom = constraints.max_zoom;
1087             self.update_zoom_transform();
1088         }
1089     }
1090 
hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel>1091     fn hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
1092         match opts::get().device_pixels_per_px {
1093             Some(device_pixels_per_px) => TypedScale::new(device_pixels_per_px),
1094             None => match opts::get().output_file {
1095                 Some(_) => TypedScale::new(1.0),
1096                 None => self.scale_factor
1097             }
1098         }
1099     }
1100 
device_pixels_per_page_px(&self) -> TypedScale<f32, CSSPixel, DevicePixel>1101     fn device_pixels_per_page_px(&self) -> TypedScale<f32, CSSPixel, DevicePixel> {
1102         self.page_zoom * self.hidpi_factor()
1103     }
1104 
update_zoom_transform(&mut self)1105     fn update_zoom_transform(&mut self) {
1106         let scale = self.device_pixels_per_page_px();
1107         self.scale = TypedScale::new(scale.get());
1108     }
1109 
on_zoom_reset_window_event(&mut self)1110     pub fn on_zoom_reset_window_event(&mut self) {
1111         self.page_zoom = TypedScale::new(1.0);
1112         self.update_zoom_transform();
1113         self.send_window_size(WindowSizeType::Resize);
1114         self.update_page_zoom_for_webrender();
1115     }
1116 
on_zoom_window_event(&mut self, magnification: f32)1117     pub fn on_zoom_window_event(&mut self, magnification: f32) {
1118         self.page_zoom = TypedScale::new((self.page_zoom.get() * magnification)
1119                                           .max(MIN_ZOOM).min(MAX_ZOOM));
1120         self.update_zoom_transform();
1121         self.send_window_size(WindowSizeType::Resize);
1122         self.update_page_zoom_for_webrender();
1123     }
1124 
update_page_zoom_for_webrender(&mut self)1125     fn update_page_zoom_for_webrender(&mut self) {
1126         let page_zoom = webrender_api::ZoomFactor::new(self.page_zoom.get());
1127 
1128         let mut txn = webrender_api::Transaction::new();
1129         txn.set_page_zoom(page_zoom);
1130         self.webrender_api.send_transaction(self.webrender_document, txn);
1131     }
1132 
1133     /// Simulate a pinch zoom
on_pinch_zoom_window_event(&mut self, magnification: f32)1134     pub fn on_pinch_zoom_window_event(&mut self, magnification: f32) {
1135         self.pending_scroll_zoom_events.push(ScrollZoomEvent {
1136             magnification: magnification,
1137             scroll_location: ScrollLocation::Delta(TypedVector2D::zero()), // TODO: Scroll to keep the center in view?
1138             cursor:  TypedPoint2D::new(-1, -1), // Make sure this hits the base layer.
1139             phase: ScrollEventPhase::Move(true),
1140             event_count: 1,
1141         });
1142     }
1143 
send_viewport_rects(&self)1144     fn send_viewport_rects(&self) {
1145         let mut scroll_states_per_pipeline = HashMap::new();
1146         for scroll_layer_state in self.webrender_api.get_scroll_node_state(self.webrender_document) {
1147             let scroll_state = ScrollState {
1148                 scroll_id: scroll_layer_state.id,
1149                 scroll_offset: scroll_layer_state.scroll_offset.to_untyped(),
1150             };
1151 
1152             scroll_states_per_pipeline
1153                 .entry(scroll_layer_state.id.pipeline_id())
1154                 .or_insert(vec![])
1155                 .push(scroll_state);
1156         }
1157 
1158         for (pipeline_id, scroll_states) in scroll_states_per_pipeline {
1159             if let Some(pipeline) = self.pipeline(pipeline_id.from_webrender()) {
1160                 let msg = LayoutControlMsg::SetScrollStates(scroll_states);
1161                 let _ = pipeline.layout_chan.send(msg);
1162             }
1163         }
1164     }
1165 
1166     // Check if any pipelines currently have active animations or animation callbacks.
animations_active(&self) -> bool1167     fn animations_active(&self) -> bool {
1168         for (_, details) in &self.pipeline_details {
1169             // If animations are currently running, then don't bother checking
1170             // with the constellation if the output image is stable.
1171             if details.animations_running {
1172                 return true;
1173             }
1174             if details.animation_callbacks_running {
1175                 return true;
1176             }
1177         }
1178 
1179         false
1180     }
1181 
1182     /// Query the constellation to see if the current compositor
1183     /// output matches the current frame tree output, and if the
1184     /// associated script threads are idle.
is_ready_to_paint_image_output(&mut self) -> Result<(), NotReadyToPaint>1185     fn is_ready_to_paint_image_output(&mut self) -> Result<(), NotReadyToPaint> {
1186         match self.ready_to_save_state {
1187             ReadyState::Unknown => {
1188                 // Unsure if the output image is stable.
1189 
1190                 // Collect the currently painted epoch of each pipeline that is
1191                 // complete (i.e. has *all* layers painted to the requested epoch).
1192                 // This gets sent to the constellation for comparison with the current
1193                 // frame tree.
1194                 let mut pipeline_epochs = HashMap::new();
1195                 for (id, _) in &self.pipeline_details {
1196                     let webrender_pipeline_id = id.to_webrender();
1197                     if let Some(webrender_api::Epoch(epoch)) = self.webrender
1198                                                                    .current_epoch(webrender_pipeline_id) {
1199                         let epoch = Epoch(epoch);
1200                         pipeline_epochs.insert(*id, epoch);
1201                     }
1202                 }
1203 
1204                 // Pass the pipeline/epoch states to the constellation and check
1205                 // if it's safe to output the image.
1206                 let msg = ConstellationMsg::IsReadyToSaveImage(pipeline_epochs);
1207                 if let Err(e) = self.constellation_chan.send(msg) {
1208                     warn!("Sending ready to save to constellation failed ({}).", e);
1209                 }
1210                 self.ready_to_save_state = ReadyState::WaitingForConstellationReply;
1211                 Err(NotReadyToPaint::JustNotifiedConstellation)
1212             }
1213             ReadyState::WaitingForConstellationReply => {
1214                 // If waiting on a reply from the constellation to the last
1215                 // query if the image is stable, then assume not ready yet.
1216                 Err(NotReadyToPaint::WaitingOnConstellation)
1217             }
1218             ReadyState::ReadyToSaveImage => {
1219                 // Constellation has replied at some point in the past
1220                 // that the current output image is stable and ready
1221                 // for saving.
1222                 // Reset the flag so that we check again in the future
1223                 // TODO: only reset this if we load a new document?
1224                 if opts::get().is_running_problem_test {
1225                     println!("was ready to save, resetting ready_to_save_state");
1226                 }
1227                 self.ready_to_save_state = ReadyState::Unknown;
1228                 Ok(())
1229             }
1230         }
1231     }
1232 
composite(&mut self)1233     pub fn composite(&mut self) {
1234         let target = self.composite_target;
1235         match self.composite_specific_target(target) {
1236             Ok(_) => if opts::get().output_file.is_some() || opts::get().exit_after_load {
1237                 println!("Shutting down the Constellation after generating an output file or exit flag specified");
1238                 self.start_shutting_down();
1239             },
1240             Err(e) => if opts::get().is_running_problem_test {
1241                 if e != UnableToComposite::NotReadyToPaintImage(NotReadyToPaint::WaitingOnConstellation) {
1242                     println!("not ready to composite: {:?}", e);
1243                 }
1244             },
1245         }
1246     }
1247 
1248     /// Composite either to the screen or to a png image or both.
1249     /// Returns Ok if composition was performed or Err if it was not possible to composite
1250     /// for some reason. If CompositeTarget is Window or Png no image data is returned;
1251     /// in the latter case the image is written directly to a file. If CompositeTarget
1252     /// is WindowAndPng Ok(Some(png::Image)) is returned.
composite_specific_target(&mut self, target: CompositeTarget) -> Result<Option<Image>, UnableToComposite>1253     fn composite_specific_target(&mut self,
1254                                  target: CompositeTarget)
1255                                  -> Result<Option<Image>, UnableToComposite> {
1256         let (width, height) =
1257             (self.frame_size.width as usize, self.frame_size.height as usize);
1258         if !self.window.prepare_for_composite(width, height) {
1259             return Err(UnableToComposite::WindowUnprepared)
1260         }
1261 
1262         self.webrender.update();
1263 
1264         let wait_for_stable_image = match target {
1265             CompositeTarget::WindowAndPng | CompositeTarget::PngFile => true,
1266             CompositeTarget::Window => opts::get().exit_after_load,
1267         };
1268 
1269         if wait_for_stable_image {
1270             // The current image may be ready to output. However, if there are animations active,
1271             // tick those instead and continue waiting for the image output to be stable AND
1272             // all active animations to complete.
1273             if self.animations_active() {
1274                 self.process_animations();
1275                 return Err(UnableToComposite::NotReadyToPaintImage(NotReadyToPaint::AnimationsActive));
1276             }
1277             if let Err(result) = self.is_ready_to_paint_image_output() {
1278                 return Err(UnableToComposite::NotReadyToPaintImage(result))
1279             }
1280         }
1281 
1282         let render_target_info = match target {
1283             CompositeTarget::Window => RenderTargetInfo::empty(),
1284             _ => initialize_png(&*self.gl, width, height)
1285         };
1286 
1287         profile(ProfilerCategory::Compositing, None, self.time_profiler_chan.clone(), || {
1288             debug!("compositor: compositing");
1289 
1290             // Paint the scene.
1291             // TODO(gw): Take notice of any errors the renderer returns!
1292             self.webrender.render(self.frame_size).ok();
1293         });
1294 
1295         // If there are pending paint metrics, we check if any of the painted epochs is
1296         // one of the ones that the paint metrics recorder is expecting . In that case,
1297         // we get the current time, inform the layout thread about it and remove the
1298         // pending metric from the list.
1299         if !self.pending_paint_metrics.is_empty() {
1300             let paint_time = precise_time_ns();
1301             let mut to_remove = Vec::new();
1302             // For each pending paint metrics pipeline id
1303             for (id, pending_epoch) in &self.pending_paint_metrics {
1304                 // we get the last painted frame id from webrender
1305                 if let Some(webrender_api::Epoch(epoch)) = self.webrender.current_epoch(id.to_webrender()) {
1306                     // and check if it is the one the layout thread is expecting,
1307                     let epoch = Epoch(epoch);
1308                     if *pending_epoch != epoch {
1309                         continue;
1310                     }
1311                     // in which case, we remove it from the list of pending metrics,
1312                     to_remove.push(id.clone());
1313                     if let Some(pipeline) = self.pipeline(*id) {
1314                         // and inform the layout thread with the measured paint time.
1315                         let msg = LayoutControlMsg::PaintMetric(epoch, paint_time);
1316                         if let Err(e)  = pipeline.layout_chan.send(msg) {
1317                             warn!("Sending PaintMetric message to layout failed ({}).", e);
1318                         }
1319                     }
1320                 }
1321             }
1322             for id in to_remove.iter() {
1323                 self.pending_paint_metrics.remove(id);
1324             }
1325         }
1326 
1327         let rv = match target {
1328             CompositeTarget::Window => None,
1329             CompositeTarget::WindowAndPng => {
1330                 let img = self.draw_img(render_target_info,
1331                                         width,
1332                                         height);
1333                 Some(Image {
1334                     width: img.width(),
1335                     height: img.height(),
1336                     format: PixelFormat::RGB8,
1337                     bytes: IpcSharedMemory::from_bytes(&*img),
1338                     id: None,
1339                 })
1340             }
1341             CompositeTarget::PngFile => {
1342                 profile(ProfilerCategory::ImageSaving, None, self.time_profiler_chan.clone(), || {
1343                     match opts::get().output_file.as_ref() {
1344                         Some(path) => match File::create(path) {
1345                             Ok(mut file) => {
1346                                 let img = self.draw_img(render_target_info, width, height);
1347                                 let dynamic_image = DynamicImage::ImageRgb8(img);
1348                                 if let Err(e) = dynamic_image.save(&mut file, ImageFormat::PNG) {
1349                                     error!("Failed to save {} ({}).", path, e);
1350                                 }
1351                             },
1352                             Err(e) => error!("Failed to create {} ({}).", path, e),
1353                         },
1354                         None => error!("No file specified."),
1355                     }
1356                 });
1357                 None
1358             }
1359         };
1360 
1361         // Perform the page flip. This will likely block for a while.
1362         self.window.present();
1363 
1364         self.last_composite_time = precise_time_ns();
1365 
1366         self.composition_request = CompositionRequest::NoCompositingNecessary;
1367 
1368         self.process_animations();
1369         self.start_scrolling_bounce_if_necessary();
1370         self.waiting_for_results_of_scroll = false;
1371 
1372         Ok(rv)
1373     }
1374 
draw_img(&self, render_target_info: RenderTargetInfo, width: usize, height: usize) -> RgbImage1375     fn draw_img(&self,
1376                 render_target_info: RenderTargetInfo,
1377                 width: usize,
1378                 height: usize)
1379                 -> RgbImage {
1380         // For some reason, OSMesa fails to render on the 3rd
1381         // attempt in headless mode, under some conditions.
1382         // I think this can only be some kind of synchronization
1383         // bug in OSMesa, but explicitly un-binding any vertex
1384         // array here seems to work around that bug.
1385         // See https://github.com/servo/servo/issues/18606.
1386         self.gl.bind_vertex_array(0);
1387 
1388         let mut pixels = self.gl.read_pixels(0, 0,
1389                                              width as gl::GLsizei,
1390                                              height as gl::GLsizei,
1391                                              gl::RGB, gl::UNSIGNED_BYTE);
1392 
1393         self.gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
1394 
1395         self.gl.delete_buffers(&render_target_info.texture_ids);
1396         self.gl.delete_renderbuffers(&render_target_info.renderbuffer_ids);
1397         self.gl.delete_framebuffers(&render_target_info.framebuffer_ids);
1398 
1399         // flip image vertically (texture is upside down)
1400         let orig_pixels = pixels.clone();
1401         let stride = width * 3;
1402         for y in 0..height {
1403             let dst_start = y * stride;
1404             let src_start = (height - y - 1) * stride;
1405             let src_slice = &orig_pixels[src_start .. src_start + stride];
1406             (&mut pixels[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]);
1407         }
1408         RgbImage::from_raw(width as u32, height as u32, pixels).expect("Flipping image failed!")
1409     }
1410 
composite_if_necessary(&mut self, reason: CompositingReason)1411     fn composite_if_necessary(&mut self, reason: CompositingReason) {
1412         if self.composition_request == CompositionRequest::NoCompositingNecessary {
1413             if opts::get().is_running_problem_test {
1414                 println!("updating composition_request ({:?})", reason);
1415             }
1416             self.composition_request = CompositionRequest::CompositeNow(reason)
1417         } else if opts::get().is_running_problem_test {
1418             println!("composition_request is already {:?}", self.composition_request);
1419         }
1420     }
1421 
get_root_pipeline_id(&self) -> Option<PipelineId>1422     fn get_root_pipeline_id(&self) -> Option<PipelineId> {
1423         self.root_pipeline.as_ref().map(|pipeline| pipeline.id)
1424     }
1425 
start_scrolling_bounce_if_necessary(&mut self)1426     fn start_scrolling_bounce_if_necessary(&mut self) {
1427         if self.scroll_in_progress {
1428             return
1429         }
1430 
1431         if self.webrender.layers_are_bouncing_back() {
1432             let mut txn = webrender_api::Transaction::new();
1433             txn.tick_scrolling_bounce_animations();
1434             self.webrender_api.send_transaction(self.webrender_document, txn);
1435             self.send_viewport_rects()
1436         }
1437     }
1438 
receive_messages(&mut self) -> bool1439     pub fn receive_messages(&mut self) -> bool {
1440         // Check for new messages coming from the other threads in the system.
1441         let mut compositor_messages = vec![];
1442         let mut found_recomposite_msg = false;
1443         while let Some(msg) = self.port.try_recv_compositor_msg() {
1444             match msg {
1445                 Msg::Recomposite(_) if found_recomposite_msg => {}
1446                 Msg::Recomposite(_) => {
1447                     found_recomposite_msg = true;
1448                     compositor_messages.push(msg)
1449                 }
1450                 _ => compositor_messages.push(msg),
1451             }
1452         }
1453         for msg in compositor_messages {
1454             if !self.handle_browser_message(msg) {
1455                 return false
1456             }
1457         }
1458         true
1459     }
1460 
perform_updates(&mut self) -> bool1461     pub fn perform_updates(&mut self) -> bool {
1462         if self.shutdown_state == ShutdownState::FinishedShuttingDown {
1463             return false;
1464         }
1465 
1466         // If a pinch-zoom happened recently, ask for tiles at the new resolution
1467         if self.zoom_action && precise_time_s() - self.zoom_time > 0.3 {
1468             self.zoom_action = false;
1469         }
1470 
1471         match self.composition_request {
1472             CompositionRequest::NoCompositingNecessary => {}
1473             CompositionRequest::CompositeNow(_) => {
1474                 self.composite()
1475             }
1476         }
1477 
1478         if !self.pending_scroll_zoom_events.is_empty() && !self.waiting_for_results_of_scroll {
1479             self.process_pending_scroll_events()
1480         }
1481         self.shutdown_state != ShutdownState::FinishedShuttingDown
1482     }
1483 
1484     /// Repaints and recomposites synchronously. You must be careful when calling this, as if a
1485     /// paint is not scheduled the compositor will hang forever.
1486     ///
1487     /// This is used when resizing the window.
repaint_synchronously(&mut self)1488     pub fn repaint_synchronously(&mut self) {
1489         while self.shutdown_state != ShutdownState::ShuttingDown {
1490             let msg = self.port.recv_compositor_msg();
1491             let need_recomposite = match msg {
1492                 Msg::Recomposite(_) => true,
1493                 _ => false,
1494             };
1495             let keep_going = self.handle_browser_message(msg);
1496             if need_recomposite {
1497                 self.composite();
1498                 break
1499             }
1500             if !keep_going {
1501                 break
1502             }
1503         }
1504     }
1505 
pinch_zoom_level(&self) -> f321506     pub fn pinch_zoom_level(&self) -> f32 {
1507         // TODO(gw): Access via WR.
1508         1.0
1509     }
1510 
toggle_webrender_debug(&mut self, option: WebRenderDebugOption)1511     pub fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) {
1512         let mut flags = self.webrender.get_debug_flags();
1513         let flag = match option {
1514             WebRenderDebugOption::Profiler => {
1515                 webrender::DebugFlags::PROFILER_DBG |
1516                 webrender::DebugFlags::GPU_TIME_QUERIES |
1517                 webrender::DebugFlags::GPU_SAMPLE_QUERIES
1518             }
1519             WebRenderDebugOption::TextureCacheDebug => {
1520                 webrender::DebugFlags::TEXTURE_CACHE_DBG
1521             }
1522             WebRenderDebugOption::RenderTargetDebug => {
1523                 webrender::DebugFlags::RENDER_TARGET_DBG
1524             }
1525         };
1526         flags.toggle(flag);
1527         self.webrender.set_debug_flags(flags);
1528 
1529         let mut txn = webrender_api::Transaction::new();
1530         txn.generate_frame();
1531         self.webrender_api.send_transaction(self.webrender_document, txn);
1532     }
1533 }
1534 
1535 /// Why we performed a composite. This is used for debugging.
1536 #[derive(Clone, Copy, Debug, PartialEq)]
1537 pub enum CompositingReason {
1538     /// We hit the delayed composition timeout. (See `delayed_composition.rs`.)
1539     DelayedCompositeTimeout,
1540     /// The window has been scrolled and we're starting the first recomposite.
1541     Scroll,
1542     /// A scroll has continued and we need to recomposite again.
1543     ContinueScroll,
1544     /// We're performing the single composite in headless mode.
1545     Headless,
1546     /// We're performing a composite to run an animation.
1547     Animation,
1548     /// A new frame tree has been loaded.
1549     NewFrameTree,
1550     /// New painted buffers have been received.
1551     NewPaintedBuffers,
1552     /// The window has been zoomed.
1553     Zoom,
1554     /// A new WebRender frame has arrived.
1555     NewWebRenderFrame,
1556     /// WebRender has processed a scroll event and has generated a new frame.
1557     NewWebRenderScrollFrame,
1558 }
1559