servo: Merge #14698 - Constellation documentation comments (from asajeffrey:constellation-docs); r=ConnorGBrewster
authorAlan Jeffrey <ajeffrey@mozilla.com>
Sat, 24 Dec 2016 05:46:40 -0800
changeset 340406 dfc3bb45f709990e63d4fb65402018d0950676c7
parent 340405 f8c4216e65487f09ce40b43bdfa1522835c97416
child 340407 5ce3a7c52a845edb5a93a043a88eac976067e844
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersConnorGBrewster
servo: Merge #14698 - Constellation documentation comments (from asajeffrey:constellation-docs); r=ConnorGBrewster <!-- Please describe your changes on the following line: --> Rewrote the documentation comments for `constellation.rs`. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes do not require tests because it's just documentation <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 02717931ad45c5ac9780edb1504bafd38039a671
servo/components/constellation/constellation.rs
--- a/servo/components/constellation/constellation.rs
+++ b/servo/components/constellation/constellation.rs
@@ -1,18 +1,71 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! The `Constellation`, Servo's Grand Central Station
 //!
-//! The primary duty of a `Constellation` is to mediate between the
-//! graphics compositor and the many `Pipeline`s in the browser's
-//! navigation context, each `Pipeline` encompassing a `ScriptThread`,
-//! `LayoutThread`, and `PaintThread`.
+//! The constellation tracks all information kept globally by the
+//! browser engine, which includes:
+//!
+//! * The set of all `EventLoop` objects. Each event loop is
+//!   the constellation's view of a script thread. The constellation
+//!   interacts with a script thread by message-passing.
+//!
+//! * The set of all `Pipeline` objects.  Each pipeline gives the
+//!   constellation's view of a `Window`, with its script thread and
+//!   layout threads.  Pipelines may share script threads, but not
+//!   layout threads.
+//!
+//! * The set of all `Frame` objects. Each frame gives the constellation's
+//!   view of a browsing context. Each browsing context stores an independent
+//!   session history, created by navigation of that frame. The session
+//!   history can be traversed, for example by the back and forwards UI,
+//!   so each session history maintains a list of past and future pipelines,
+//!   as well as the current active pipeline.
+//!
+//! There are two kinds of frames: top-level frames (for example tabs
+//! in a browser UI), and nested frames (typically caused by `iframe`
+//! elements). Frames have a hierarchy (typically caused by `iframe`s
+//! containing `iframe`s), giving rise to a frame tree with a root frame.
+//! The logical relationship between these types is:
+//!
+//! ```
+//! +---------+              +------------+                 +-------------+
+//! |  Frame  | --parent?--> |  Pipeline  | --event_loop--> |  EventLoop  |
+//! |         | --current--> |            |                 |             |
+//! |         | --prev*----> |            | <---pipeline*-- |             |
+//! |         | --next*----> |            |                 +-------------+
+//! |         |              |            |
+//! |         | <----frame-- |            |
+//! +---------+              +------------+
+//! ```
+//
+//! Complicating matters, there are also mozbrowser iframes, which are top-level
+//! frames with a parent.
+//!
+//! The constellation also maintains channels to threads, including:
+//!
+//! * The script and layout threads.
+//! * The graphics compositor.
+//! * The font cache, image cache, and resource manager, which load
+//!   and cache shared fonts, images, or other resources.
+//! * The service worker manager.
+//! * The devtools, debugger and webdriver servers.
+//!
+//! The constellation passes messages between the threads, and updates its state
+//! to track the evolving state of the frame tree.
+//!
+//! The constellation acts as a logger, tracking any `warn!` messages from threads,
+//! and converting any `error!` or `panic!` into a crash report, which is filed
+//! using an appropriate `mozbrowsererror` event.
+//!
+//! Since there is only one constellation, and its responsibilities include crash reporting,
+//! it is very important that it does not panic.
 
 use backtrace::Backtrace;
 use bluetooth_traits::BluetoothRequest;
 use canvas::canvas_paint_thread::CanvasPaintThread;
 use canvas::webgl_paint_thread::WebGLPaintThread;
 use canvas_traits::CanvasMsg;
 use compositing::SendableFrameTree;
 use compositing::compositor_thread::CompositorProxy;
@@ -65,232 +118,328 @@ use std::sync::mpsc::{Receiver, Sender, 
 use std::thread;
 use std::time::Instant;
 use style_traits::PagePx;
 use style_traits::cursor::Cursor;
 use style_traits::viewport::ViewportConstraints;
 use timer_scheduler::TimerScheduler;
 use webrender_traits;
 
-#[derive(Debug, PartialEq)]
-enum ReadyToSave {
-    NoRootFrame,
-    PendingFrames,
-    WebFontNotLoaded,
-    DocumentLoading,
-    EpochMismatch,
-    PipelineUnknown,
-    Ready,
-}
-
-/// Maintains the pipelines and navigation context and grants permission to composite.
+/// The `Constellation` itself. In the servo browser, there is one
+/// constellation, which maintains all of the browser global data.
+/// In embedded applications, there may be more than one constellation,
+/// which are independent of each other.
+///
+/// The constellation may be in a different process from the pipelines,
+/// and communicates using IPC.
 ///
 /// It is parameterized over a `LayoutThreadFactory` and a
 /// `ScriptThreadFactory` (which in practice are implemented by
 /// `LayoutThread` in the `layout` crate, and `ScriptThread` in
-/// the `script` crate).
+/// the `script` crate). Script and layout communicate using a `Message`
+/// type.
 pub struct Constellation<Message, LTF, STF> {
-    /// A channel through which script messages can be sent to this object.
+    /// An IPC channel for script threads to send messages to the constellation.
+    /// This is the script threads' view of `script_receiver`.
     script_sender: IpcSender<FromScriptMsg>,
 
-    /// A channel through which layout thread messages can be sent to this object.
+    /// A channel for the constellation to receive messages from script threads.
+    /// This is the constellation's view of `script_sender`.
+    script_receiver: Receiver<FromScriptMsg>,
+
+    /// An IPC channel for layout threads to send messages to the constellation.
+    /// This is the layout threads' view of `layout_receiver`.
     layout_sender: IpcSender<FromLayoutMsg>,
 
-    /// Receives messages from scripts.
-    script_receiver: Receiver<FromScriptMsg>,
-
-    /// Receives messages from the compositor
-    compositor_receiver: Receiver<FromCompositorMsg>,
-
-    /// Receives messages from the layout thread
+    /// A channel for the constellation to receive messages from layout threads.
+    /// This is the constellation's view of `layout_sender`.
     layout_receiver: Receiver<FromLayoutMsg>,
 
-    /// A channel (the implementation of which is port-specific) through which messages can be sent
-    /// to the compositor.
+    /// A channel for the constellation to receive messages from the compositor thread.
+    compositor_receiver: Receiver<FromCompositorMsg>,
+
+    /// A channel (the implementation of which is port-specific) for the
+    /// constellation to send messages to the compositor thread.
     compositor_proxy: Box<CompositorProxy>,
 
-    /// Channels through which messages can be sent to the resource-related threads.
+    /// Channels for the constellation to send messages to the public
+    /// resource-related threads.  There are two groups of resource
+    /// threads: one for public browsing, and one for private
+    /// browsing.
     public_resource_threads: ResourceThreads,
 
-    /// Channels through which messages can be sent to the resource-related threads.
+    /// Channels for the constellation to send messages to the private
+    /// resource-related threads.  There are two groups of resource
+    /// threads: one for public browsing, and one for private
+    /// browsing.
     private_resource_threads: ResourceThreads,
 
-    /// A channel through which messages can be sent to the image cache thread.
+    /// A channel for the constellation to send messages to the image
+    /// cache thread.
     image_cache_thread: ImageCacheThread,
 
-    /// A channel through which messages can be sent to the debugger.
+    /// A channel for the constellation to send messages to the font
+    /// cache thread.
+    font_cache_thread: FontCacheThread,
+
+    /// A channel for the constellation to send messages to the
+    /// debugger thread.
     debugger_chan: Option<debugger::Sender>,
 
-    /// A channel through which messages can be sent to the developer tools.
+    /// A channel for the constellation to send messages to the
+    /// devtools thread.
     devtools_chan: Option<Sender<DevtoolsControlMsg>>,
 
-    /// A channel through which messages can be sent to the bluetooth thread.
+    /// An IPC channel for the constellation to send messages to the
+    /// bluetooth thread.
     bluetooth_thread: IpcSender<BluetoothRequest>,
 
-    /// Sender to Service Worker Manager thread
+    /// An IPC channel for the constellation to send messages to the
+    /// Service Worker Manager thread.
     swmanager_chan: Option<IpcSender<ServiceWorkerMsg>>,
 
-    /// to send messages to this object
+    /// An IPC channel for Service Worker Manager threads to send
+    /// messages to the constellation.  This is the SW Manager thread's
+    /// view of `swmanager_receiver`.
     swmanager_sender: IpcSender<SWManagerMsg>,
 
-    /// to receive sw manager message
+    /// A channel for the constellation to receive messages from the
+    /// Service Worker Manager thread. This is the constellation's view of
+    /// `swmanager_sender`.
     swmanager_receiver: Receiver<SWManagerMsg>,
 
-    /// A map from top-level frame id and registered domain name to event loops.
-    /// This double indirection ensures that separate tabs do not share event loops,
-    /// even if the same domain is loaded in each.
+    /// A channel for the constellation to send messages to the
+    /// time profiler thread.
+    time_profiler_chan: time::ProfilerChan,
+
+    /// A channel for the constellation to send messages to the
+    /// memory profiler thread.
+    mem_profiler_chan: mem::ProfilerChan,
+
+    /// A channel for the constellation to send messages to the
+    /// timer thread.
+    scheduler_chan: IpcSender<TimerEventRequest>,
+
+    /// A channel for the constellation to send messages to the
+    /// Webrender thread.
+    webrender_api_sender: webrender_traits::RenderApiSender,
+
+    /// The set of all event loops in the browser. We generate a new
+    /// event loop for each registered domain name (aka eTLD+1) in
+    /// each top-level frame. We store the event loops in a map
+    /// indexed by top-level frame id (as a `FrameId`) and registered
+    /// domain name (as a `String`) to event loops. This double
+    /// indirection ensures that separate tabs do not share event
+    /// loops, even if the same domain is loaded in each.
+    /// It is important that scripts with the same eTLD+1
+    /// share an event loop, since they can use `document.domain`
+    /// to become same-origin, at which point they can share DOM objects.
     event_loops: HashMap<FrameId, HashMap<String, Weak<EventLoop>>>,
 
-    /// A list of all the pipelines. (See the `pipeline` module for more details.)
+    /// The set of all the pipelines in the browser.
+    /// (See the `pipeline` module for more details.)
     pipelines: HashMap<PipelineId, Pipeline>,
 
-    /// A list of all the frames
+    /// The set of all the frames in the browser.
     frames: HashMap<FrameId, Frame>,
 
-    /// A channel through which messages can be sent to the font cache.
-    font_cache_thread: FontCacheThread,
+    /// When a navigation is performed, we do not immediately update
+    /// the frame tree, instead we ask the event loop to begin loading
+    /// the new document, and do not update the frame tree until the
+    /// document is active. Between starting the load and it activating,
+    /// we store a `FrameChange` object for the navigation in progress.
+    pending_frames: Vec<FrameChange>,
 
-    /// ID of the root frame.
+    /// The root frame.
     root_frame_id: FrameId,
 
-    /// The next free ID to assign to a pipeline ID namespace.
-    next_pipeline_namespace_id: PipelineNamespaceId,
-
-    /// Pipeline ID that has currently focused element for key events.
+    /// The currently focused pipeline for key events.
     focus_pipeline_id: Option<PipelineId>,
 
-    /// Navigation operations that are in progress.
-    pending_frames: Vec<FrameChange>,
-
-    /// A channel through which messages can be sent to the time profiler.
-    time_profiler_chan: time::ProfilerChan,
+    /// Pipeline IDs are namespaced in order to avoid name collisions,
+    /// and the namespaces are allocated by the constellation.
+    next_pipeline_namespace_id: PipelineNamespaceId,
 
-    /// A channel through which messages can be sent to the memory profiler.
-    mem_profiler_chan: mem::ProfilerChan,
-
-    phantom: PhantomData<(Message, LTF, STF)>,
-
+    /// The size of the top-level window.
     window_size: WindowSizeData,
 
     /// Bits of state used to interact with the webdriver implementation
     webdriver: WebDriverData,
 
-    scheduler_chan: IpcSender<TimerEventRequest>,
-
     /// Document states for loaded pipelines (used only when writing screenshots).
     document_states: HashMap<PipelineId, DocumentState>,
 
-    // Webrender interface.
-    webrender_api_sender: webrender_traits::RenderApiSender,
-
     /// Are we shutting down?
     shutting_down: bool,
 
     /// Have we seen any warnings? Hopefully always empty!
     /// The buffer contains `(thread_name, reason)` entries.
     handled_warnings: VecDeque<(Option<String>, String)>,
 
     /// The random number generator and probability for closing pipelines.
     /// This is for testing the hardening of the constellation.
     random_pipeline_closure: Option<(StdRng, f32)>,
+
+    /// Phantom data that keeps the Rust type system happy.
+    phantom: PhantomData<(Message, LTF, STF)>,
 }
 
 /// State needed to construct a constellation.
 pub struct InitialConstellationState {
     /// A channel through which messages can be sent to the compositor.
     pub compositor_proxy: Box<CompositorProxy + Send>,
+
     /// A channel to the debugger, if applicable.
     pub debugger_chan: Option<debugger::Sender>,
+
     /// A channel to the developer tools, if applicable.
     pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
+
     /// A channel to the bluetooth thread.
     pub bluetooth_thread: IpcSender<BluetoothRequest>,
+
     /// A channel to the image cache thread.
     pub image_cache_thread: ImageCacheThread,
+
     /// A channel to the font cache thread.
     pub font_cache_thread: FontCacheThread,
+
     /// A channel to the resource thread.
     pub public_resource_threads: ResourceThreads,
+
     /// A channel to the resource thread.
     pub private_resource_threads: ResourceThreads,
+
     /// A channel to the time profiler thread.
     pub time_profiler_chan: time::ProfilerChan,
+
     /// A channel to the memory profiler thread.
     pub mem_profiler_chan: mem::ProfilerChan,
-    /// Whether the constellation supports the clipboard.
-    pub supports_clipboard: bool,
+
     /// Webrender API.
     pub webrender_api_sender: webrender_traits::RenderApiSender,
+
+    /// Whether the constellation supports the clipboard.
+    /// TODO: this field is not used, remove it?
+    pub supports_clipboard: bool,
+}
+
+/// A frame in the frame tree.
+/// Each frame is the constrellation's view of a browsing context.
+/// Each browsing context has a session history, caused by
+/// navigation and traversing the history. Each frame has its
+/// current entry, plus past and future entries. The past is sorted
+/// chronologically, the future is sorted reverse chronoogically:
+/// in partiucular prev.pop() is the latest past entry, and
+/// next.pop() is the earliest future entry.
+#[derive(Debug, Clone)]
+struct Frame {
+    /// The frame id.
+    id: FrameId,
+
+    /// The past session history, ordered chronologically.
+    prev: Vec<FrameState>,
+
+    /// The currently active session history entry.
+    current: FrameState,
+
+    /// The future session history, ordered reverse chronologically.
+    next: Vec<FrameState>,
 }
 
+impl Frame {
+    /// Create a new frame.
+    /// Note this just creates the frame, it doesn't add it to the frame tree.
+    fn new(id: FrameId, pipeline_id: PipelineId) -> Frame {
+        Frame {
+            id: id,
+            prev: vec!(),
+            current: FrameState::new(pipeline_id, id),
+            next: vec!(),
+        }
+    }
+
+    /// Set the current frame entry, and push the current frame entry into the past.
+    fn load(&mut self, pipeline_id: PipelineId) {
+        self.prev.push(self.current.clone());
+        self.current = FrameState::new(pipeline_id, self.id);
+    }
+
+    /// Set the future to be empty.
+    fn remove_forward_entries(&mut self) -> Vec<FrameState> {
+        replace(&mut self.next, vec!())
+    }
+
+    /// Set the current frame entry, and drop the current frame entry.
+    fn replace_current(&mut self, pipeline_id: PipelineId) -> FrameState {
+        replace(&mut self.current, FrameState::new(pipeline_id, self.id))
+    }
+}
+
+/// An entry in a frame's session history.
+/// Each entry stores the pipeline id for a document in the session history.
+/// When we operate on the joint session history, entries are sorted chronologically,
+/// so we timestamp the entries by when the entry was added to the session history.
 #[derive(Debug, Clone)]
 struct FrameState {
+    /// The timestamp for when the session history entry was created
     instant: Instant,
+    /// The pipeline for the document in the session history
     pipeline_id: PipelineId,
+    /// The frame that this session history entry is part of
     frame_id: FrameId,
 }
 
 impl FrameState {
+    /// Create a new session history entry.
     fn new(pipeline_id: PipelineId, frame_id: FrameId) -> FrameState {
         FrameState {
             instant: Instant::now(),
             pipeline_id: pipeline_id,
             frame_id: frame_id,
         }
     }
 }
 
-/// Stores the navigation context for a single frame in the frame tree.
-struct Frame {
-    id: FrameId,
-    prev: Vec<FrameState>,
-    current: FrameState,
-    next: Vec<FrameState>,
-}
-
-impl Frame {
-    fn new(id: FrameId, pipeline_id: PipelineId) -> Frame {
-        Frame {
-            id: id,
-            prev: vec!(),
-            current: FrameState::new(pipeline_id, id),
-            next: vec!(),
-        }
-    }
-
-    fn load(&mut self, pipeline_id: PipelineId) {
-        self.prev.push(self.current.clone());
-        self.current = FrameState::new(pipeline_id, self.id);
-    }
-
-    fn remove_forward_entries(&mut self) -> Vec<FrameState> {
-        replace(&mut self.next, vec!())
-    }
-
-    fn replace_current(&mut self, pipeline_id: PipelineId) -> FrameState {
-        replace(&mut self.current, FrameState::new(pipeline_id, self.id))
-    }
-}
-
 /// Represents a pending change in the frame tree, that will be applied
 /// once the new pipeline has loaded and completed initial layout / paint.
 struct FrameChange {
+    /// The frame to change.
     frame_id: FrameId,
+
+    /// The pipeline that was currently active at the time the change started.
+    /// TODO: can this field be removed?
     old_pipeline_id: Option<PipelineId>,
+
+    /// The pipeline for the document being loaded.
     new_pipeline_id: PipelineId,
+
+    /// Is this document ready to be activated?
+    /// TODO: this flag is never set, it can be removed.
     document_ready: bool,
+
+    /// Is the new document replacing the current document (e.g. a reload)
+    /// or pushing it into the session history (e.g. a navigation)?
     replace: bool,
 }
 
-/// An iterator over a frame tree, returning nodes in depth-first order.
-/// Note that this iterator should _not_ be used to mutate nodes _during_
-/// iteration. Mutating nodes once the iterator is out of scope is OK.
+/// An iterator over a frame tree, returning the fully active frames in
+/// depth-first order. Note that this iterator only returns the fully
+/// active frames, that is ones where every ancestor frame is
+/// in the currently active pipeline of its parent frame.
 struct FrameTreeIterator<'a> {
+    /// The frames still to iterate over.
     stack: Vec<FrameId>,
+
+    /// The set of all frames.
     frames: &'a HashMap<FrameId, Frame>,
+
+    /// The set of all pipelines.  We use this to find the active
+    /// children of a frame, which are the iframes in the currently
+    /// active document.
     pipelines: &'a HashMap<PipelineId, Pipeline>,
 }
 
 impl<'a> Iterator for FrameTreeIterator<'a> {
     type Item = &'a Frame;
     fn next(&mut self) -> Option<&'a Frame> {
         loop {
             let frame_id = match self.stack.pop() {
@@ -312,19 +461,29 @@ impl<'a> Iterator for FrameTreeIterator<
                 },
             };
             self.stack.extend(pipeline.children.iter().map(|&c| c));
             return Some(frame)
         }
     }
 }
 
+/// An iterator over a frame tree, returning all frames in depth-first
+/// order. Note that this iterator returns all frames, not just the
+/// fully active ones.
 struct FullFrameTreeIterator<'a> {
+    /// The frames still to iterate over.
     stack: Vec<FrameId>,
+
+    /// The set of all frames.
     frames: &'a HashMap<FrameId, Frame>,
+
+    /// The set of all pipelines.  We use this to find the
+    /// children of a frame, which are the iframes in all documents
+    /// in the session history.
     pipelines: &'a HashMap<PipelineId, Pipeline>,
 }
 
 impl<'a> Iterator for FullFrameTreeIterator<'a> {
     type Item = &'a Frame;
     fn next(&mut self) -> Option<&'a Frame> {
         loop {
             let frame_id = match self.stack.pop() {
@@ -343,36 +502,58 @@ impl<'a> Iterator for FullFrameTreeItera
                     self.stack.extend(pipeline.children.iter().map(|&c| c));
                 }
             }
             return Some(frame)
         }
     }
 }
 
+/// Data needed for webdriver
 struct WebDriverData {
     load_channel: Option<(PipelineId, IpcSender<webdriver_msg::LoadStatus>)>,
     resize_channel: Option<IpcSender<WindowSizeData>>,
 }
 
 impl WebDriverData {
     fn new() -> WebDriverData {
         WebDriverData {
             load_channel: None,
             resize_channel: None,
         }
     }
 }
 
+/// When we are running reftests, we save an image to compare against a reference.
+/// This enum gives the possible states of preparing such an image.
+#[derive(Debug, PartialEq)]
+enum ReadyToSave {
+    NoRootFrame,
+    PendingFrames,
+    WebFontNotLoaded,
+    DocumentLoading,
+    EpochMismatch,
+    PipelineUnknown,
+    Ready,
+}
+
+/// When we are exiting a pipeline, we can either force exiting or not.
+/// A normal exit waits for the compositor to update its state before
+/// exiting, and delegates layout exit to script. A forced exit does
+/// not notify the compositor, and exits layout without involving script.
 #[derive(Clone, Copy)]
 enum ExitPipelineMode {
     Normal,
     Force,
 }
 
+/// The constellation uses logging to perform crash reporting.
+/// The constellation receives all `warn!`, `error!` and `panic!` messages,
+/// and generates a crash report when it receives a panic.
+
 /// A logger directed at the constellation from content processes
 #[derive(Clone)]
 pub struct FromScriptLogger {
     /// A channel to the constellation
     pub constellation_chan: Arc<ReentrantMutex<IpcSender<FromScriptMsg>>>,
 }
 
 impl FromScriptLogger {
@@ -439,46 +620,52 @@ impl Log for FromCompositorLogger {
             let thread_name = thread::current().name().map(ToOwned::to_owned);
             let msg = FromCompositorMsg::LogEntry(top_level_frame_id, thread_name, entry);
             let chan = self.constellation_chan.lock().unwrap_or_else(|err| err.into_inner());
             let _ = chan.send(msg);
         }
     }
 }
 
+/// Rust uses `LogRecord` for storing logging, but servo converts that to
+/// a `LogEntry`. We do this so that we can record panics as well as log
+/// messages, and because `LogRecord` does not implement serde (de)serialization,
+/// so cannot be used over an IPC channel.
 fn log_entry(record: &LogRecord) -> Option<LogEntry> {
     match record.level() {
         LogLevel::Error if thread::panicking() => Some(LogEntry::Panic(
             format!("{}", record.args()),
             format!("{:?}", Backtrace::new())
         )),
         LogLevel::Error => Some(LogEntry::Error(
             format!("{}", record.args())
         )),
         LogLevel::Warn => Some(LogEntry::Warn(
             format!("{}", record.args())
         )),
         _ => None,
     }
 }
 
+/// The number of warnings to include in each crash report.
 const WARNINGS_BUFFER_SIZE: usize = 32;
 
 /// The registered domain name (aka eTLD+1) for a URL.
 /// Returns None if the URL has no host name.
 /// Returns the registered suffix for the host name if it is a domain.
 /// Leaves the host name alone if it is an IP address.
 fn reg_host<'a>(url: &'a ServoUrl) -> Option<&'a str> {
     url.domain().map(reg_suffix).or(url.host_str())
 }
 
 impl<Message, LTF, STF> Constellation<Message, LTF, STF>
     where LTF: LayoutThreadFactory<Message=Message>,
           STF: ScriptThreadFactory<Message=Message>
 {
+    /// Create a new constellation thread.
     pub fn start(state: InitialConstellationState) -> (Sender<FromCompositorMsg>, IpcSender<SWManagerMsg>) {
         let (compositor_sender, compositor_receiver) = channel();
 
         // service worker manager to communicate with constellation
         let (swmanager_sender, swmanager_receiver) = ipc::channel().expect("ipc channel failure");
         let sw_mgr_clone = swmanager_sender.clone();
 
         thread::Builder::new().name("Constellation".to_owned()).spawn(move || {
@@ -544,26 +731,28 @@ impl<Message, LTF, STF> Constellation<Me
             };
 
             constellation.run();
         }).expect("Thread spawning failed");
 
         (compositor_sender, swmanager_sender)
     }
 
+    /// The main event loop for the constellation.
     fn run(&mut self) {
         while !self.shutting_down || !self.pipelines.is_empty() {
             // Randomly close a pipeline if --random-pipeline-closure-probability is set
             // This is for testing the hardening of the constellation.
             self.maybe_close_random_pipeline();
             self.handle_request();
         }
         self.handle_shutdown();
     }
 
+    /// Generate a new pipeline id namespace.
     fn next_pipeline_namespace_id(&mut self) -> PipelineNamespaceId {
         let namespace_id = self.next_pipeline_namespace_id;
         let PipelineNamespaceId(ref mut i) = self.next_pipeline_namespace_id;
         *i += 1;
         namespace_id
     }
 
     /// Helper function for creating a pipeline
@@ -661,70 +850,80 @@ impl<Message, LTF, STF> Constellation<Me
                 .or_insert_with(HashMap::new)
                 .insert(host, Rc::downgrade(&pipeline.event_loop));
         }
 
         assert!(!self.pipelines.contains_key(&pipeline_id));
         self.pipelines.insert(pipeline_id, pipeline);
     }
 
-    // Get an iterator for the current frame tree. Specify self.root_frame_id to
-    // iterate the entire tree, or a specific frame id to iterate only that sub-tree.
+    /// Get an iterator for the current frame tree. Specify self.root_frame_id to
+    /// iterate the entire tree, or a specific frame id to iterate only that sub-tree.
+    /// Iterates over the fully active frames in the tree.
     fn current_frame_tree_iter(&self, frame_id_root: FrameId) -> FrameTreeIterator {
         FrameTreeIterator {
             stack: vec!(frame_id_root),
             pipelines: &self.pipelines,
             frames: &self.frames,
         }
     }
 
+    /// Get an iterator for the current frame tree. Specify self.root_frame_id to
+    /// iterate the entire tree, or a specific frame id to iterate only that sub-tree.
+    /// Iterates over all frames in the tree.
     fn full_frame_tree_iter(&self, frame_id_root: FrameId) -> FullFrameTreeIterator {
         FullFrameTreeIterator {
             stack: vec!(frame_id_root),
             pipelines: &self.pipelines,
             frames: &self.frames,
         }
     }
 
+    /// The joint session future is the merge of the session future of every
+    /// frame in the frame tree, sorted reverse chronologically.
     fn joint_session_future(&self, frame_id_root: FrameId) -> Vec<(Instant, FrameId, PipelineId)> {
         let mut future = vec!();
         for frame in self.full_frame_tree_iter(frame_id_root) {
             future.extend(frame.next.iter().map(|entry| (entry.instant, entry.frame_id, entry.pipeline_id)));
         }
 
         // reverse sorting
         future.sort_by(|a, b| b.cmp(a));
         future
     }
 
+    /// Is the joint session future empty?
     fn joint_session_future_is_empty(&self, frame_id_root: FrameId) -> bool {
         self.full_frame_tree_iter(frame_id_root)
             .all(|frame| frame.next.is_empty())
     }
 
+    /// The joint session past is the merge of the session past of every
+    /// frame in the frame tree, sorted chronologically.
     fn joint_session_past(&self, frame_id_root: FrameId) -> Vec<(Instant, FrameId, PipelineId)> {
         let mut past = vec!();
         for frame in self.full_frame_tree_iter(frame_id_root) {
             let mut prev_instant = frame.current.instant;
             for entry in frame.prev.iter().rev() {
                 past.push((prev_instant, entry.frame_id, entry.pipeline_id));
                 prev_instant = entry.instant;
             }
         }
 
         past.sort();
         past
     }
 
+    /// Is the joint session past empty?
     fn joint_session_past_is_empty(&self, frame_id_root: FrameId) -> bool {
         self.full_frame_tree_iter(frame_id_root)
             .all(|frame| frame.prev.is_empty())
     }
 
-    // Create a new frame and update the internal bookkeeping.
+    /// Create a new frame and update the internal bookkeeping.
     fn new_frame(&mut self, frame_id: FrameId, pipeline_id: PipelineId) {
         let frame = Frame::new(frame_id, pipeline_id);
         self.frames.insert(frame_id, frame);
 
         // If a child frame, add it to the parent pipeline.
         let parent_info = self.pipelines.get(&pipeline_id)
             .and_then(|pipeline| pipeline.parent_info);
         if let Some((parent_id, _)) = parent_info {