servo: Merge #13498 - IFrame elements now manage FrameIds rather than the constellation (from asajeffrey:script-iframe-stores-frameid); r=glennw
authorAlan Jeffrey <ajeffrey@mozilla.com>
Fri, 07 Oct 2016 14:54:31 -0500
changeset 339854 a235117279ca1918a965b8930553a1e6dc0d2084
parent 339853 8ca9059c239001a27f8fbce456f4e3162b5c019e
child 339855 aaafef97c958772ec7260c14a840f7d6a8b8b29d
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)
reviewersglennw
servo: Merge #13498 - IFrame elements now manage FrameIds rather than the constellation (from asajeffrey:script-iframe-stores-frameid); r=glennw <!-- Please describe your changes on the following line: --> This PR stores the FrameId as well as the PipelineId in an html iframe. The iframes are now responsible for creating frame ids, not the constellation. This is the first step in fixing #633, because it means we know the frame id of each script thread when it is created. It also means we can share the frame id, for example using it in the debugger. cc @jdm, @ConnorGBrewster and @ejpbruel. --- <!-- 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 a refactoring. <!-- 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: d01a8662154924a551c8d7a2f12b22bcaf998a7c
servo/components/constellation/constellation.rs
servo/components/msg/constellation_msg.rs
servo/components/script/dom/bindings/trace.rs
servo/components/script/dom/htmliframeelement.rs
servo/components/script_traits/lib.rs
servo/tests/unit/net/fetch.rs
servo/tests/unit/net/http_loader.rs
--- a/servo/components/constellation/constellation.rs
+++ b/servo/components/constellation/constellation.rs
@@ -134,24 +134,21 @@ pub struct Constellation<Message, LTF, S
 
     /// A list of all the frames
     frames: HashMap<FrameId, Frame>,
 
     /// A channel through which messages can be sent to the font cache.
     font_cache_thread: FontCacheThread,
 
     /// ID of the root frame.
-    root_frame_id: Option<FrameId>,
+    root_frame_id: FrameId,
 
     /// The next free ID to assign to a pipeline ID namespace.
     next_pipeline_namespace_id: PipelineNamespaceId,
 
-    /// The next free ID to assign to a frame.
-    next_frame_id: FrameId,
-
     /// Pipeline ID that has currently focused element 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,
@@ -263,16 +260,17 @@ impl Frame {
     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 {
+    frame_id: FrameId,
     old_pipeline_id: Option<PipelineId>,
     new_pipeline_id: PipelineId,
     document_ready: bool,
     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_
@@ -471,16 +469,18 @@ impl<Message, LTF, STF> Constellation<Me
             let (ipc_script_sender, ipc_script_receiver) = ipc::channel().expect("ipc channel failure");
             let script_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_script_receiver);
 
             let (ipc_layout_sender, ipc_layout_receiver) = ipc::channel().expect("ipc channel failure");
             let layout_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_layout_receiver);
 
             let swmanager_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(swmanager_receiver);
 
+            PipelineNamespace::install(PipelineNamespaceId(0));
+
             let mut constellation: Constellation<Message, LTF, STF> = Constellation {
                 script_sender: ipc_script_sender,
                 layout_sender: ipc_layout_sender,
                 script_receiver: script_receiver,
                 compositor_receiver: compositor_receiver,
                 layout_receiver: layout_receiver,
                 compositor_proxy: state.compositor_proxy,
                 devtools_chan: state.devtools_chan,
@@ -490,19 +490,19 @@ impl<Message, LTF, STF> Constellation<Me
                 image_cache_thread: state.image_cache_thread,
                 font_cache_thread: state.font_cache_thread,
                 swmanager_chan: None,
                 swmanager_receiver: swmanager_receiver,
                 swmanager_sender: sw_mgr_clone,
                 pipelines: HashMap::new(),
                 frames: HashMap::new(),
                 pending_frames: vec!(),
-                next_pipeline_namespace_id: PipelineNamespaceId(0),
-                root_frame_id: None,
-                next_frame_id: FrameId(0),
+                // We initialize the namespace at 1, since we reserved namespace 0 for the constellation
+                next_pipeline_namespace_id: PipelineNamespaceId(1),
+                root_frame_id: FrameId::new(),
                 focus_pipeline_id: None,
                 time_profiler_chan: state.time_profiler_chan,
                 mem_profiler_chan: state.mem_profiler_chan,
                 window_size: WindowSizeData {
                     visible_viewport: opts::get().initial_window_size.to_f32() *
                                           ScaleFactor::new(1.0),
                     initial_viewport: opts::get().initial_window_size.to_f32() *
                         ScaleFactor::new(1.0),
@@ -520,18 +520,17 @@ impl<Message, LTF, STF> Constellation<Me
                 random_pipeline_closure: opts::get().random_pipeline_closure_probability.map(|prob| {
                     let seed = opts::get().random_pipeline_closure_seed.unwrap_or_else(random);
                     let rng = StdRng::from_seed(&[seed]);
                     warn!("Randomly closing pipelines.");
                     info!("Using seed {} for random pipeline closure.", seed);
                     (rng, prob)
                 }),
             };
-            let namespace_id = constellation.next_pipeline_namespace_id();
-            PipelineNamespace::install(namespace_id);
+
             constellation.run();
         });
         (compositor_sender, swmanager_sender)
     }
 
     fn run(&mut self) {
         while !self.shutting_down || !self.pipelines.is_empty() {
             // Randomly close a pipeline if --random-pipeline-closure-probability is set
@@ -609,19 +608,19 @@ impl<Message, LTF, STF> Constellation<Me
         }
 
         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.
-    fn current_frame_tree_iter(&self, frame_id_root: Option<FrameId>) -> FrameTreeIterator {
+    fn current_frame_tree_iter(&self, frame_id_root: FrameId) -> FrameTreeIterator {
         FrameTreeIterator {
-            stack: frame_id_root.into_iter().collect(),
+            stack: vec!(frame_id_root),
             pipelines: &self.pipelines,
             frames: &self.frames,
         }
     }
 
     fn full_frame_tree_iter(&self, frame_id_root: FrameId) -> FullFrameTreeIterator {
         FullFrameTreeIterator {
             stack: vec!(frame_id_root),
@@ -651,30 +650,24 @@ impl<Message, LTF, STF> Constellation<Me
             }
         }
 
         past.sort();
         past
     }
 
     // Create a new frame and update the internal bookkeeping.
-    fn new_frame(&mut self, pipeline_id: PipelineId) -> FrameId {
-        let id = self.next_frame_id;
-        let FrameId(ref mut i) = self.next_frame_id;
-        *i += 1;
-
-        let frame = Frame::new(id, pipeline_id);
+    fn new_frame(&mut self, frame_id: FrameId, pipeline_id: PipelineId) {
+        let frame = Frame::new(frame_id, pipeline_id);
 
         assert!(self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.frame).is_none());
-        assert!(!self.frames.contains_key(&id));
+        assert!(!self.frames.contains_key(&frame_id));
 
-        self.pipelines.get_mut(&pipeline_id).map(|pipeline| pipeline.frame = Some(id));
-        self.frames.insert(id, frame);
-
-        id
+        self.pipelines.get_mut(&pipeline_id).map(|pipeline| pipeline.frame = Some(frame_id));
+        self.frames.insert(frame_id, frame);
     }
 
     /// Handles loading pages, navigation, and granting access to the compositor
     #[allow(unsafe_code)]
     fn handle_request(&mut self) {
         enum Request {
             Script(FromScriptMsg),
             Compositor(FromCompositorMsg),
@@ -1027,20 +1020,19 @@ impl<Message, LTF, STF> Constellation<Me
         }
     }
 
     fn handle_exit(&mut self) {
         // TODO: add a timer, which forces shutdown if threads aren't responsive.
         if self.shutting_down { return; }
         self.shutting_down = true;
 
-        // TODO: exit before the root frame is set?
-        if let Some(root_id) = self.root_frame_id {
-            self.close_frame(root_id, ExitPipelineMode::Normal);
-        }
+        // TODO: exit before the root frame is initialized?
+        let root_frame_id = self.root_frame_id;
+        self.close_frame(root_frame_id, ExitPipelineMode::Normal);
     }
 
     fn handle_shutdown(&mut self) {
         // At this point, there are no active pipelines,
         // so we can safely block on other threads, without worrying about deadlock.
         // Channels to recieve signals when threads are done exiting.
         let (core_sender, core_receiver) = ipc::channel().expect("Failed to create IPC channel!");
         let (storage_sender, storage_receiver) = ipc::channel().expect("Failed to create IPC channel!");
@@ -1116,16 +1108,17 @@ impl<Message, LTF, STF> Constellation<Me
 
         // Notify the browser chrome that the pipeline has failed
         self.trigger_mozbrowsererror(pipeline_id, reason, backtrace);
 
         if let Some(pipeline_id) = pipeline_id {
             let pipeline_url = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.url.clone());
             let parent_info = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.parent_info);
             let window_size = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.size);
+            let frame_id = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.frame);
 
             self.close_pipeline(pipeline_id, ExitPipelineMode::Force);
             self.pipelines.remove(&pipeline_id);
 
             while let Some(pending_pipeline_id) = self.pending_frames.iter().find(|pending| {
                 pending.old_pipeline_id == Some(pipeline_id)
             }).map(|frame| frame.new_pipeline_id) {
                 warn!("removing pending frame change for failed pipeline");
@@ -1137,26 +1130,29 @@ impl<Message, LTF, STF> Constellation<Me
             if let Some(pipeline_url) = pipeline_url {
                 if pipeline_url == failure_url {
                     return error!("about:failure failed");
                 }
             }
 
             warn!("creating replacement pipeline for about:failure");
 
-            let new_pipeline_id = PipelineId::new();
-            let load_data = LoadData::new(failure_url, None, None);
-            self.new_pipeline(new_pipeline_id, parent_info, Some(pipeline_id), window_size, None, load_data, false);
+            if let Some(frame_id) = frame_id {
+                let new_pipeline_id = PipelineId::new();
+                let load_data = LoadData::new(failure_url, None, None);
+                self.new_pipeline(new_pipeline_id, parent_info, Some(pipeline_id), window_size, None, load_data, false);
 
-            self.pending_frames.push(FrameChange {
-                old_pipeline_id: Some(pipeline_id),
-                new_pipeline_id: new_pipeline_id,
-                document_ready: false,
-                replace: false,
-            });
+                self.pending_frames.push(FrameChange {
+                    frame_id: frame_id,
+                    old_pipeline_id: Some(pipeline_id),
+                    new_pipeline_id: new_pipeline_id,
+                    document_ready: false,
+                    replace: false,
+                });
+            }
         }
     }
 
     fn handle_log_entry(&mut self, pipeline_id: Option<PipelineId>, thread_name: Option<String>, entry: LogEntry) {
         debug!("Received log entry {:?}.", entry);
         match entry {
             LogEntry::Panic(reason, backtrace) => self.handle_panic(pipeline_id, reason, Some(backtrace)),
             LogEntry::Error(reason) | LogEntry::Warn(reason) => {
@@ -1167,21 +1163,21 @@ impl<Message, LTF, STF> Constellation<Me
                 self.handled_warnings.push_back((thread_name, reason));
             },
         }
     }
 
     fn handle_init_load(&mut self, url: Url) {
         let window_size = self.window_size.visible_viewport;
         let root_pipeline_id = PipelineId::new();
-        debug_assert!(PipelineId::fake_root_pipeline_id() == root_pipeline_id);
         self.new_pipeline(root_pipeline_id, None, None, Some(window_size), None,
                           LoadData::new(url.clone(), None, None), false);
         self.handle_load_start_msg(root_pipeline_id);
         self.pending_frames.push(FrameChange {
+            frame_id: self.root_frame_id,
             old_pipeline_id: None,
             new_pipeline_id: root_pipeline_id,
             document_ready: false,
             replace: false,
         });
         self.compositor_proxy.send(ToCompositorMsg::ChangePageUrl(root_pipeline_id, url));
     }
 
@@ -1299,16 +1295,17 @@ impl<Message, LTF, STF> Constellation<Me
                           Some((load_info.parent_pipeline_id, load_info.frame_type)),
                           load_info.old_pipeline_id,
                           window_size,
                           script_chan,
                           load_data,
                           is_private);
 
         self.pending_frames.push(FrameChange {
+            frame_id: load_info.frame_id,
             old_pipeline_id: load_info.old_pipeline_id,
             new_pipeline_id: load_info.new_pipeline_id,
             document_ready: false,
             replace: load_info.replace,
         });
     }
 
     fn handle_set_cursor_msg(&mut self, cursor: Cursor) {
@@ -1346,18 +1343,17 @@ impl<Message, LTF, STF> Constellation<Me
 
     fn handle_alert(&mut self,
                     pipeline_id: PipelineId,
                     message: String,
                     sender: IpcSender<bool>) {
         let display_alert_dialog = if PREFS.is_mozbrowser_enabled() {
             let parent_pipeline_info = self.pipelines.get(&pipeline_id).and_then(|source| source.parent_info);
             if parent_pipeline_info.is_some() {
-                let root_pipeline_id = self.root_frame_id
-                    .and_then(|root_frame_id| self.frames.get(&root_frame_id))
+                let root_pipeline_id = self.frames.get(&self.root_frame_id)
                     .map(|root_frame| root_frame.current.pipeline_id);
 
                 let ancestor_info = self.get_mozbrowser_ancestor_info(pipeline_id);
                 if let Some((ancestor_id, mozbrowser_iframe_id)) = ancestor_info {
                     if root_pipeline_id == Some(ancestor_id) {
                         match root_pipeline_id.and_then(|pipeline_id| self.pipelines.get(&pipeline_id)) {
                             Some(root_pipeline) => {
                                 // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsershowmodalprompt
@@ -1436,16 +1432,17 @@ impl<Message, LTF, STF> Constellation<Me
                 // Being here means either there are no pending frames, or none of the pending
                 // changes would be overridden by changing the subframe associated with source_id.
 
                 // Create the new pipeline
                 let window_size = self.pipelines.get(&source_id).and_then(|source| source.size);
                 let new_pipeline_id = PipelineId::new();
                 self.new_pipeline(new_pipeline_id, None, None, window_size, None, load_data, false);
                 self.pending_frames.push(FrameChange {
+                    frame_id: self.root_frame_id,
                     old_pipeline_id: Some(source_id),
                     new_pipeline_id: new_pipeline_id,
                     document_ready: false,
                     replace: replace,
                 });
 
                 // Send message to ScriptThread that will suspend all timers
                 match self.pipelines.get(&source_id) {
@@ -1453,51 +1450,46 @@ impl<Message, LTF, STF> Constellation<Me
                     None => warn!("Pipeline {:?} loaded after closure", source_id),
                 };
                 Some(new_pipeline_id)
             }
         }
     }
 
     fn handle_load_start_msg(&mut self, pipeline_id: PipelineId) {
-        if let Some(frame_id) = self.get_top_level_frame_for_pipeline(Some(pipeline_id)) {
-            let forward = !self.joint_session_future(frame_id).is_empty();
-            let back = !self.joint_session_past(frame_id).is_empty();
-            self.compositor_proxy.send(ToCompositorMsg::LoadStart(back, forward));
-        }
+        let frame_id = self.get_top_level_frame_for_pipeline(Some(pipeline_id));
+        let forward = !self.joint_session_future(frame_id).is_empty();
+        let back = !self.joint_session_past(frame_id).is_empty();
+        self.compositor_proxy.send(ToCompositorMsg::LoadStart(back, forward));
     }
 
     fn handle_load_complete_msg(&mut self, pipeline_id: PipelineId) {
         let mut webdriver_reset = false;
         if let Some((expected_pipeline_id, ref reply_chan)) = self.webdriver.load_channel {
             debug!("Sending load to WebDriver");
             if expected_pipeline_id == pipeline_id {
                 let _ = reply_chan.send(webdriver_msg::LoadStatus::LoadComplete);
                 webdriver_reset = true;
             }
         }
         if webdriver_reset {
             self.webdriver.load_channel = None;
         }
-        if let Some(frame_id) = self.get_top_level_frame_for_pipeline(Some(pipeline_id)) {
-            let forward = !self.joint_session_future(frame_id).is_empty();
-            let back = !self.joint_session_past(frame_id).is_empty();
-            let root = self.root_frame_id.is_none() || self.root_frame_id == Some(frame_id);
-            self.compositor_proxy.send(ToCompositorMsg::LoadComplete(back, forward, root));
-        }
+        let frame_id = self.get_top_level_frame_for_pipeline(Some(pipeline_id));
+        let forward = !self.joint_session_future(frame_id).is_empty();
+        let back = !self.joint_session_past(frame_id).is_empty();
+        let root = self.root_frame_id == frame_id;
+        self.compositor_proxy.send(ToCompositorMsg::LoadComplete(back, forward, root));
         self.handle_subframe_loaded(pipeline_id);
     }
 
     fn handle_traverse_history_msg(&mut self,
                                    pipeline_id: Option<PipelineId>,
                                    direction: TraversalDirection) {
-        let frame_id = match self.get_top_level_frame_for_pipeline(pipeline_id) {
-            Some(frame_id) => frame_id,
-            None => return warn!("Traverse message received after root's closure."),
-        };
+        let frame_id = self.get_top_level_frame_for_pipeline(pipeline_id);
 
         let mut traversal_info = HashMap::new();
 
         match direction {
             TraversalDirection::Forward(delta) => {
                 let mut future = self.joint_session_future(frame_id);
                 for _ in 0..delta {
                     match future.pop() {
@@ -1521,40 +1513,32 @@ impl<Message, LTF, STF> Constellation<Me
             },
         };
         for (frame_id, pipeline_id) in traversal_info {
             self.traverse_frame_to_pipeline(frame_id, pipeline_id);
         }
     }
 
     fn handle_joint_session_history_length(&self, pipeline_id: PipelineId, sender: IpcSender<u32>) {
-        let frame_id = match self.get_top_level_frame_for_pipeline(Some(pipeline_id)) {
-            Some(frame_id) => frame_id,
-            None => {
-                warn!("Jsh length message received after root's closure.");
-                let _ = sender.send(0);
-                return;
-            },
-        };
+        let frame_id = self.get_top_level_frame_for_pipeline(Some(pipeline_id));
 
         // Initialize length at 1 to count for the current active entry
         let mut length = 1;
         for frame in self.full_frame_tree_iter(frame_id) {
             length += frame.next.len();
             length += frame.prev.len();
         }
         let _ = sender.send(length as u32);
     }
 
     fn handle_key_msg(&mut self, ch: Option<char>, key: Key, state: KeyState, mods: KeyModifiers) {
         // Send to the explicitly focused pipeline (if it exists), or the root
         // frame's current pipeline. If neither exist, fall back to sending to
         // the compositor below.
-        let root_pipeline_id = self.root_frame_id
-            .and_then(|root_frame_id| self.frames.get(&root_frame_id))
+        let root_pipeline_id = self.frames.get(&self.root_frame_id)
             .map(|root_frame| root_frame.current.pipeline_id);
         let pipeline_id = self.focus_pipeline_id.or(root_pipeline_id);
 
         match pipeline_id {
             Some(pipeline_id) => {
                 let event = CompositorEvent::KeyEvent(ch, key, state, mods);
                 let msg = ConstellationControlMsg::SendEvent(pipeline_id, event);
                 let result = match self.pipelines.get(&pipeline_id) {
@@ -1569,18 +1553,17 @@ impl<Message, LTF, STF> Constellation<Me
                 let event = ToCompositorMsg::KeyEvent(ch, key, state, mods);
                 self.compositor_proxy.clone_compositor_proxy().send(event);
             }
         }
     }
 
     fn handle_reload_msg(&mut self) {
         // Send Reload constellation msg to root script channel.
-        let root_pipeline_id = self.root_frame_id
-            .and_then(|root_frame_id| self.frames.get(&root_frame_id))
+        let root_pipeline_id = self.frames.get(&self.root_frame_id)
             .map(|root_frame| root_frame.current.pipeline_id);
 
         if let Some(pipeline_id) = root_pipeline_id {
             let msg = ConstellationControlMsg::Reload(pipeline_id);
             let result = match self.pipelines.get(&pipeline_id) {
                 Some(pipeline) => pipeline.script_chan.send(msg),
                 None => return debug!("Pipeline {:?} got reload event after closure.", pipeline_id),
             };
@@ -1613,18 +1596,18 @@ impl<Message, LTF, STF> Constellation<Me
         match self.pipelines.get(&parent_pipeline_id) {
             Some(pipeline) => pipeline.trigger_mozbrowser_event(pipeline_id, event),
             None => warn!("Pipeline {:?} handling mozbrowser event after closure.", parent_pipeline_id),
         }
     }
 
     fn handle_get_pipeline(&mut self, frame_id: Option<FrameId>,
                            resp_chan: IpcSender<Option<(PipelineId, bool)>>) {
-        let current_pipeline_id = frame_id.or(self.root_frame_id)
-            .and_then(|frame_id| self.frames.get(&frame_id))
+        let frame_id = frame_id.unwrap_or(self.root_frame_id);
+        let current_pipeline_id = self.frames.get(&frame_id)
             .map(|frame| frame.current.pipeline_id);
         let current_pipeline_id_loaded = current_pipeline_id
             .map(|id| (id, true));
         let pipeline_id_loaded = self.pending_frames.iter().rev()
             .find(|x| x.old_pipeline_id == current_pipeline_id)
             .map(|x| (x.new_pipeline_id, x.document_ready))
             .or(current_pipeline_id_loaded);
         if let Err(e) = resp_chan.send(pipeline_id_loaded) {
@@ -1788,18 +1771,17 @@ impl<Message, LTF, STF> Constellation<Me
                     let event = CompositorEvent::KeyEvent(None, key, state, mods);
                     let control_msg = ConstellationControlMsg::SendEvent(pipeline_id, event);
                     if let Err(e) = script_channel.send(control_msg) {
                         return self.handle_send_error(pipeline_id, e);
                     }
                 }
             },
             WebDriverCommandMsg::TakeScreenshot(pipeline_id, reply) => {
-                let current_pipeline_id = self.root_frame_id
-                    .and_then(|root_frame_id| self.frames.get(&root_frame_id))
+                let current_pipeline_id = self.frames.get(&self.root_frame_id)
                     .map(|root_frame| root_frame.current.pipeline_id);
                 if Some(pipeline_id) == current_pipeline_id {
                     self.compositor_proxy.send(ToCompositorMsg::CreatePng(reply));
                 } else {
                     if let Err(e) = reply.send(None) {
                         warn!("Screenshot reply failed ({})", e);
                     }
                 }
@@ -1893,22 +1875,22 @@ impl<Message, LTF, STF> Constellation<Me
             }
 
             // If this is an iframe, send a mozbrowser location change event.
             // This is the result of a back/forward traversal.
             self.trigger_mozbrowserlocationchange(next_pipeline_id);
         }
     }
 
-    fn get_top_level_frame_for_pipeline(&self, pipeline_id: Option<PipelineId>) -> Option<FrameId> {
+    fn get_top_level_frame_for_pipeline(&self, pipeline_id: Option<PipelineId>) -> FrameId {
         if PREFS.is_mozbrowser_enabled() {
             pipeline_id.and_then(|id| self.get_mozbrowser_ancestor_info(id))
                        .and_then(|pipeline_info| self.pipelines.get(&pipeline_info.1))
                        .and_then(|pipeline| pipeline.frame)
-                       .or(self.root_frame_id)
+                       .unwrap_or(self.root_frame_id)
         } else {
             // If mozbrowser is not enabled, the root frame is the only top-level frame
             self.root_frame_id
         }
     }
 
     fn load_url_for_webdriver(&mut self,
                               pipeline_id: PipelineId,
@@ -1928,75 +1910,62 @@ impl<Message, LTF, STF> Constellation<Me
         if let Some(old_pipeline_id) = frame_change.old_pipeline_id {
             if let Some(old_frame_id) = self.pipelines.get(&old_pipeline_id).and_then(|pipeline| pipeline.frame) {
                 if self.focused_pipeline_in_tree(old_frame_id) {
                     self.focus_pipeline_id = Some(frame_change.new_pipeline_id);
                 }
             }
         }
 
-        let frame_id = frame_change.old_pipeline_id.and_then(|old_pipeline_id| {
+        if let Some(old_pipeline_id) = frame_change.old_pipeline_id {
             // The new pipeline is replacing an old one.
             // Remove paint permissions for the pipeline being replaced.
             self.revoke_paint_permission(old_pipeline_id);
-
-            self.pipelines.get(&old_pipeline_id).and_then(|pipeline| pipeline.frame)
-        });
+        };
 
-        match frame_id {
-            Some(frame_id) => {
-                // Add new pipeline to navigation frame, and return frames evicted from history.
-                if let Some(ref mut pipeline) = self.pipelines.get_mut(&frame_change.new_pipeline_id) {
-                    pipeline.frame = Some(frame_id);
-                }
+        if self.frames.contains_key(&frame_change.frame_id) {
+            // Add new pipeline to navigation frame, and return frames evicted from history.
+            if let Some(ref mut pipeline) = self.pipelines.get_mut(&frame_change.new_pipeline_id) {
+                pipeline.frame = Some(frame_change.frame_id);
+            }
 
-                if frame_change.replace {
-                    let evicted = self.frames.get_mut(&frame_id).map(|frame| {
-                        frame.replace_current(frame_change.new_pipeline_id)
-                    });
-                    if let Some(evicted) = evicted {
-                        self.close_pipeline(evicted.pipeline_id, ExitPipelineMode::Normal);
-                    }
-                } else {
-                    if let Some(ref mut frame) = self.frames.get_mut(&frame_id) {
-                        frame.load(frame_change.new_pipeline_id);
-                    }
+            if frame_change.replace {
+                let evicted = self.frames.get_mut(&frame_change.frame_id).map(|frame| {
+                    frame.replace_current(frame_change.new_pipeline_id)
+                });
+                if let Some(evicted) = evicted {
+                    self.close_pipeline(evicted.pipeline_id, ExitPipelineMode::Normal);
+                }
+            } else {
+                if let Some(ref mut frame) = self.frames.get_mut(&frame_change.frame_id) {
+                    frame.load(frame_change.new_pipeline_id);
                 }
             }
-            None => {
-                // The new pipeline is in a new frame with no history
-                let frame_id = self.new_frame(frame_change.new_pipeline_id);
+        } else {
+            // The new pipeline is in a new frame with no history
+            self.new_frame(frame_change.frame_id, frame_change.new_pipeline_id);
 
-                // If a child frame, add it to the parent pipeline. Otherwise
-                // it must surely be the root frame being created!
-                match self.pipelines.get(&frame_change.new_pipeline_id)
-                                    .and_then(|pipeline| pipeline.parent_info) {
-                    Some((parent_id, _)) => {
-                        if let Some(parent) = self.pipelines.get_mut(&parent_id) {
-                            parent.add_child(frame_id);
-                        }
-                    }
-                    None => {
-                        assert!(self.root_frame_id.is_none());
-                        self.root_frame_id = Some(frame_id);
-                    }
+            // If a child frame, add it to the parent pipeline. Otherwise
+            // it must surely be the root frame being created!
+            let parent_info = self.pipelines.get(&frame_change.new_pipeline_id)
+                .and_then(|pipeline| pipeline.parent_info);
+            if let Some((parent_id, _)) = parent_info {
+                if let Some(parent) = self.pipelines.get_mut(&parent_id) {
+                    parent.add_child(frame_change.frame_id);
                 }
             }
         }
 
         if !frame_change.replace {
             // If this is an iframe, send a mozbrowser location change event.
             // This is the result of a link being clicked and a navigation completing.
             self.trigger_mozbrowserlocationchange(frame_change.new_pipeline_id);
 
-            let frame_id = match self.get_top_level_frame_for_pipeline(Some(frame_change.new_pipeline_id)) {
-                Some(frame_id) => frame_id,
-                None => return warn!("Tried to remove forward history after root frame closure."),
-            };
-            self.clear_joint_session_future(frame_id);
+            let top_level_frame_id = self.get_top_level_frame_for_pipeline(Some(frame_change.new_pipeline_id));
+            self.clear_joint_session_future(top_level_frame_id);
         }
 
         // Build frame tree and send permission
         self.send_frame_tree_and_grant_paint_permission();
     }
 
     fn handle_activate_document_msg(&mut self, pipeline_id: PipelineId) {
         debug!("Document ready to activate {:?}", pipeline_id);
@@ -2047,23 +2016,19 @@ impl<Message, LTF, STF> Constellation<Me
         }
     }
 
     /// Called when the window is resized.
     fn handle_window_size_msg(&mut self, new_size: WindowSizeData, size_type: WindowSizeType) {
         debug!("handle_window_size_msg: {:?} {:?}", new_size.initial_viewport.to_untyped(),
                                                        new_size.visible_viewport.to_untyped());
 
-        if let Some(root_frame_id) = self.root_frame_id {
+        if let Some(frame) = self.frames.get(&self.root_frame_id) {
             // Send Resize (or ResizeInactive) messages to each
             // pipeline in the frame tree.
-            let frame = match self.frames.get(&root_frame_id) {
-                None => return warn!("Frame {:?} resized after closing.", root_frame_id),
-                Some(frame) => frame,
-            };
             let pipeline_id = frame.current.pipeline_id;
             let pipeline = match self.pipelines.get(&pipeline_id) {
                 None => return warn!("Pipeline {:?} resized after closing.", pipeline_id),
                 Some(pipeline) => pipeline,
             };
             let _ = pipeline.script_chan.send(ConstellationControlMsg::Resize(
                 pipeline.id,
                 new_size,
@@ -2122,17 +2087,17 @@ impl<Message, LTF, STF> Constellation<Me
     fn handle_is_ready_to_save_image(&mut self,
                                      pipeline_states: HashMap<PipelineId, Epoch>) -> ReadyToSave {
         // Note that this function can panic, due to ipc-channel creation failure.
         // avoiding this panic would require a mechanism for dealing
         // with low-resource scenarios.
         //
         // If there is no root frame yet, the initial page has
         // not loaded, so there is nothing to save yet.
-        if self.root_frame_id.is_none() {
+        if !self.frames.contains_key(&self.root_frame_id) {
             return ReadyToSave::NoRootFrame;
         }
 
         // If there are pending loads, wait for those to complete.
         if !self.pending_frames.is_empty() {
             return ReadyToSave::PendingFrames;
         }
 
@@ -2379,37 +2344,36 @@ impl<Message, LTF, STF> Constellation<Me
 
                 frame_tree
             })
         })
     }
 
     // Revoke paint permission from a pipeline, and all children.
     fn revoke_paint_permission(&self, pipeline_id: PipelineId) {
-        let frame_id = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.frame);
-        for frame in self.current_frame_tree_iter(frame_id) {
-            self.pipelines.get(&frame.current.pipeline_id).map(|pipeline| pipeline.revoke_paint_permission());
+        if let Some(frame_id) = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.frame) {
+            for frame in self.current_frame_tree_iter(frame_id) {
+                self.pipelines.get(&frame.current.pipeline_id).map(|pipeline| pipeline.revoke_paint_permission());
+            }
         }
     }
 
     // Send the current frame tree to compositor, and grant paint
     // permission to each pipeline in the current frame tree.
     fn send_frame_tree_and_grant_paint_permission(&mut self) {
         // Note that this function can panic, due to ipc-channel creation failure.
         // avoiding this panic would require a mechanism for dealing
         // with low-resource scenarios.
-        if let Some(root_frame_id) = self.root_frame_id {
-            if let Some(frame_tree) = self.frame_to_sendable(root_frame_id) {
-                let (chan, port) = ipc::channel().expect("Failed to create IPC channel!");
-                self.compositor_proxy.send(ToCompositorMsg::SetFrameTree(frame_tree,
-                                                                         chan));
-                if port.recv().is_err() {
-                    warn!("Compositor has discarded SetFrameTree");
-                    return; // Our message has been discarded, probably shutting down.
-                }
+        if let Some(frame_tree) = self.frame_to_sendable(self.root_frame_id) {
+            let (chan, port) = ipc::channel().expect("Failed to create IPC channel!");
+            self.compositor_proxy.send(ToCompositorMsg::SetFrameTree(frame_tree,
+                                                                     chan));
+            if port.recv().is_err() {
+                warn!("Compositor has discarded SetFrameTree");
+                return; // Our message has been discarded, probably shutting down.
             }
         }
 
         for frame in self.current_frame_tree_iter(self.root_frame_id) {
             self.pipelines.get(&frame.current.pipeline_id).map(|pipeline| pipeline.grant_paint_permission());
         }
     }
 
@@ -2484,37 +2448,35 @@ impl<Message, LTF, STF> Constellation<Me
         if let Some(pipeline_id) = pipeline_id {
             if let Some((ancestor_id, mozbrowser_iframe_id)) = self.get_mozbrowser_ancestor_info(pipeline_id) {
                 if let Some(ancestor) = self.pipelines.get(&ancestor_id) {
                     return ancestor.trigger_mozbrowser_event(Some(mozbrowser_iframe_id), event);
                 }
             }
         }
 
-        if let Some(root_frame_id) = self.root_frame_id {
-            if let Some(root_frame) = self.frames.get(&root_frame_id) {
-                if let Some(root_pipeline) = self.pipelines.get(&root_frame.current.pipeline_id) {
-                    return root_pipeline.trigger_mozbrowser_event(None, event);
-                }
+        if let Some(root_frame) = self.frames.get(&self.root_frame_id) {
+            if let Some(root_pipeline) = self.pipelines.get(&root_frame.current.pipeline_id) {
+                return root_pipeline.trigger_mozbrowser_event(None, event);
             }
         }
 
         warn!("Mozbrowser error after root pipeline closed.");
     }
 
     fn focused_pipeline_in_tree(&self, frame_id: FrameId) -> bool {
         self.focus_pipeline_id.map_or(false, |pipeline_id| {
-            self.pipeline_exists_in_tree(pipeline_id, Some(frame_id))
+            self.pipeline_exists_in_tree(pipeline_id, frame_id)
         })
     }
 
     fn pipeline_is_in_current_frame(&self, pipeline_id: PipelineId) -> bool {
         self.pipeline_exists_in_tree(pipeline_id, self.root_frame_id)
     }
 
     fn pipeline_exists_in_tree(&self,
                                pipeline_id: PipelineId,
-                               root_frame_id: Option<FrameId>) -> bool {
+                               root_frame_id: FrameId) -> bool {
         self.current_frame_tree_iter(root_frame_id)
             .any(|current_frame| current_frame.current.pipeline_id == pipeline_id)
     }
 
 }
--- a/servo/components/msg/constellation_msg.rs
+++ b/servo/components/msg/constellation_msg.rs
@@ -216,19 +216,16 @@ impl LoadData {
 }
 
 #[derive(Clone, PartialEq, Eq, Copy, Hash, Debug, Deserialize, Serialize)]
 pub enum TraversalDirection {
     Forward(usize),
     Back(usize),
 }
 
-#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug, Deserialize, Serialize, PartialOrd, Ord)]
-pub struct FrameId(pub u32);
-
 /// Each pipeline ID needs to be unique. However, it also needs to be possible to
 /// generate the pipeline ID from an iframe element (this simplifies a lot of other
 /// code that makes use of pipeline IDs).
 ///
 /// To achieve this, each pipeline index belongs to a particular namespace. There is
 /// a namespace for the constellation thread, and also one for every script thread.
 /// This allows pipeline IDs to be generated by any of those threads without conflicting
 /// with pipeline IDs created by other script threads or the constellation. The
@@ -237,40 +234,48 @@ pub struct FrameId(pub u32);
 ///
 /// It may help conceptually to think of the namespace ID as an identifier for the
 /// thread that created this pipeline ID - however this is really an implementation
 /// detail so shouldn't be relied upon in code logic. It's best to think of the
 /// pipeline ID as a simple unique identifier that doesn't convey any more information.
 #[derive(Clone, Copy)]
 pub struct PipelineNamespace {
     id: PipelineNamespaceId,
-    next_index: PipelineIndex,
+    index: u32,
 }
 
 impl PipelineNamespace {
     pub fn install(namespace_id: PipelineNamespaceId) {
         PIPELINE_NAMESPACE.with(|tls| {
             assert!(tls.get().is_none());
             tls.set(Some(PipelineNamespace {
                 id: namespace_id,
-                next_index: PipelineIndex(0),
+                index: 0,
             }));
         });
     }
 
-    fn next(&mut self) -> PipelineId {
-        let pipeline_id = PipelineId {
+    fn next_index(&mut self) -> u32 {
+        let result = self.index;
+        self.index = result + 1;
+        result
+    }
+
+    fn next_pipeline_id(&mut self) -> PipelineId {
+        PipelineId {
             namespace_id: self.id,
-            index: self.next_index,
-        };
+            index: PipelineIndex(self.next_index()),
+        }
+    }
 
-        let PipelineIndex(current_index) = self.next_index;
-        self.next_index = PipelineIndex(current_index + 1);
-
-        pipeline_id
+    fn next_frame_id(&mut self) -> FrameId {
+        FrameId {
+            namespace_id: self.id,
+            index: FrameIndex(self.next_index()),
+        }
     }
 }
 
 thread_local!(pub static PIPELINE_NAMESPACE: Cell<Option<PipelineNamespace>> = Cell::new(None));
 
 thread_local!(pub static PIPELINE_ID: Cell<Option<PipelineId>> = Cell::new(None));
 
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)]
@@ -284,34 +289,22 @@ pub struct PipelineId {
     pub namespace_id: PipelineNamespaceId,
     pub index: PipelineIndex
 }
 
 impl PipelineId {
     pub fn new() -> PipelineId {
         PIPELINE_NAMESPACE.with(|tls| {
             let mut namespace = tls.get().expect("No namespace set for this thread!");
-            let new_pipeline_id = namespace.next();
+            let new_pipeline_id = namespace.next_pipeline_id();
             tls.set(Some(namespace));
             new_pipeline_id
         })
     }
 
-    // TODO(gw): This should be removed. It's only required because of the code
-    // that uses it in the devtools lib.rs file (which itself is a TODO). Once
-    // that is fixed, this should be removed. It also relies on the first
-    // call to PipelineId::new() returning (0,0), which is checked with an
-    // assert in handle_init_load().
-    pub fn fake_root_pipeline_id() -> PipelineId {
-        PipelineId {
-            namespace_id: PipelineNamespaceId(0),
-            index: PipelineIndex(0),
-        }
-    }
-
     pub fn to_webrender(&self) -> webrender_traits::PipelineId {
         let PipelineNamespaceId(namespace_id) = self.namespace_id;
         let PipelineIndex(index) = self.index;
         webrender_traits::PipelineId(namespace_id, index)
     }
 
     pub fn install(id: PipelineId) {
         PIPELINE_ID.with(|tls| tls.set(Some(id)))
@@ -326,16 +319,51 @@ impl PipelineId {
 impl fmt::Display for PipelineId {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         let PipelineNamespaceId(namespace_id) = self.namespace_id;
         let PipelineIndex(index) = self.index;
         write!(fmt, "({},{})", namespace_id, index)
     }
 }
 
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)]
+pub struct FrameIndex(pub u32);
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)]
+pub struct FrameId {
+    pub namespace_id: PipelineNamespaceId,
+    pub index: FrameIndex
+}
+
+impl FrameId {
+    pub fn new() -> FrameId {
+        PIPELINE_NAMESPACE.with(|tls| {
+            let mut namespace = tls.get().expect("No namespace set for this thread!");
+            let new_frame_id = namespace.next_frame_id();
+            tls.set(Some(namespace));
+            new_frame_id
+        })
+    }
+}
+
+impl fmt::Display for FrameId {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        let PipelineNamespaceId(namespace_id) = self.namespace_id;
+        let FrameIndex(index) = self.index;
+        write!(fmt, "({},{})", namespace_id, index)
+    }
+}
+
+// We provide ids just for unit testing.
+pub const TEST_NAMESPACE: PipelineNamespaceId = PipelineNamespaceId(1234);
+pub const TEST_PIPELINE_INDEX: PipelineIndex = PipelineIndex(5678);
+pub const TEST_PIPELINE_ID: PipelineId = PipelineId { namespace_id: TEST_NAMESPACE, index: TEST_PIPELINE_INDEX };
+pub const TEST_FRAME_INDEX: FrameIndex = FrameIndex(8765);
+pub const TEST_FRAME_ID: FrameId = FrameId { namespace_id: TEST_NAMESPACE, index: TEST_FRAME_INDEX };
+
 #[derive(Clone, PartialEq, Eq, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)]
 pub enum FrameType {
     IFrame,
     MozBrowserIFrame,
 }
 
 /// [Policies](https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-states)
 /// for providing a referrer header for a request
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -52,17 +52,17 @@ use hyper::method::Method;
 use hyper::mime::Mime;
 use hyper::status::StatusCode;
 use ipc_channel::ipc::{IpcReceiver, IpcSender};
 use js::glue::{CallObjectTracer, CallUnbarrieredObjectTracer, CallValueTracer};
 use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind};
 use js::jsval::JSVal;
 use js::rust::Runtime;
 use libc;
-use msg::constellation_msg::{FrameType, PipelineId, ReferrerPolicy, WindowSizeType};
+use msg::constellation_msg::{FrameId, FrameType, PipelineId, ReferrerPolicy, WindowSizeType};
 use net_traits::{Metadata, NetworkError, ResourceThreads};
 use net_traits::filemanager_thread::RelativePos;
 use net_traits::image::base::{Image, ImageMetadata};
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
 use net_traits::request::Request;
 use net_traits::response::{Response, ResponseBody};
 use net_traits::response::HttpsState;
 use net_traits::storage_thread::StorageType;
@@ -303,17 +303,17 @@ no_jsmanaged_fields!(Metadata);
 no_jsmanaged_fields!(NetworkError);
 no_jsmanaged_fields!(Atom, Namespace, QualName);
 no_jsmanaged_fields!(Trusted<T: Reflectable>);
 no_jsmanaged_fields!(TrustedPromise);
 no_jsmanaged_fields!(PropertyDeclarationBlock);
 no_jsmanaged_fields!(HashSet<T>);
 // These three are interdependent, if you plan to put jsmanaged data
 // in one of these make sure it is propagated properly to containing structs
-no_jsmanaged_fields!(FrameType, WindowSizeData, WindowSizeType, PipelineId);
+no_jsmanaged_fields!(FrameId, FrameType, WindowSizeData, WindowSizeType, PipelineId);
 no_jsmanaged_fields!(TimerEventId, TimerSource);
 no_jsmanaged_fields!(WorkerId);
 no_jsmanaged_fields!(QuirksMode);
 no_jsmanaged_fields!(Runtime);
 no_jsmanaged_fields!(Headers, Method);
 no_jsmanaged_fields!(WindowProxyHandler);
 no_jsmanaged_fields!(UntrustedNodeAddress);
 no_jsmanaged_fields!(LengthOrPercentageOrAuto);
--- a/servo/components/script/dom/htmliframeelement.rs
+++ b/servo/components/script/dom/htmliframeelement.rs
@@ -33,17 +33,17 @@ use dom::globalscope::GlobalScope;
 use dom::htmlelement::HTMLElement;
 use dom::node::{Node, NodeDamage, UnbindContext, document_from_node, window_from_node};
 use dom::urlhelper::UrlHelper;
 use dom::virtualmethods::VirtualMethods;
 use dom::window::{ReflowReason, Window};
 use ipc_channel::ipc;
 use js::jsapi::{JSAutoCompartment, JSContext, MutableHandleValue};
 use js::jsval::{NullValue, UndefinedValue};
-use msg::constellation_msg::{FrameType, LoadData, PipelineId, TraversalDirection};
+use msg::constellation_msg::{FrameType, FrameId, LoadData, PipelineId, TraversalDirection};
 use net_traits::response::HttpsState;
 use script_layout_interface::message::ReflowQueryType;
 use script_traits::{IFrameLoadInfo, MozBrowserEvent, ScriptMsg as ConstellationMsg};
 use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
 use std::cell::Cell;
 use string_cache::Atom;
 use style::attr::{AttrValue, LengthOrPercentageOrAuto};
 use style::context::ReflowGoal;
@@ -62,16 +62,17 @@ bitflags! {
         const ALLOW_POINTER_LOCK = 0x10,
         const ALLOW_POPUPS = 0x20
     }
 }
 
 #[dom_struct]
 pub struct HTMLIFrameElement {
     htmlelement: HTMLElement,
+    frame_id: FrameId,
     pipeline_id: Cell<Option<PipelineId>>,
     sandbox: MutNullableHeap<JS<DOMTokenList>>,
     sandbox_allowance: Cell<Option<SandboxAllowance>>,
     load_blocker: DOMRefCell<Option<LoadBlocker>>,
     visibility: Cell<bool>,
 }
 
 impl HTMLIFrameElement {
@@ -125,16 +126,17 @@ impl HTMLIFrameElement {
         let (old_pipeline_id, new_pipeline_id) = self.generate_new_pipeline_id();
         let private_iframe = self.privatebrowsing();
         let frame_type = if self.Mozbrowser() { FrameType::MozBrowserIFrame } else { FrameType::IFrame };
 
         let global_scope = window.upcast::<GlobalScope>();
         let load_info = IFrameLoadInfo {
             load_data: load_data,
             parent_pipeline_id: global_scope.pipeline_id(),
+            frame_id: self.frame_id,
             old_pipeline_id: old_pipeline_id,
             new_pipeline_id: new_pipeline_id,
             sandbox: sandboxed,
             is_private: private_iframe,
             frame_type: frame_type,
             replace: replace,
         };
         global_scope
@@ -176,16 +178,17 @@ impl HTMLIFrameElement {
         self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
     }
 
     fn new_inherited(local_name: Atom,
                      prefix: Option<DOMString>,
                      document: &Document) -> HTMLIFrameElement {
         HTMLIFrameElement {
             htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
+            frame_id: FrameId::new(),
             pipeline_id: Cell::new(None),
             sandbox: Default::default(),
             sandbox_allowance: Cell::new(None),
             load_blocker: DOMRefCell::new(None),
             visibility: Cell::new(true),
         }
     }
 
--- a/servo/components/script_traits/lib.rs
+++ b/servo/components/script_traits/lib.rs
@@ -444,16 +444,18 @@ pub enum IFrameSandboxState {
 
 /// Specifies the information required to load a URL in an iframe.
 #[derive(Deserialize, Serialize)]
 pub struct IFrameLoadInfo {
     /// Load data containing the url to load
     pub load_data: Option<LoadData>,
     /// Pipeline ID of the parent of this iframe
     pub parent_pipeline_id: PipelineId,
+    /// The ID for this iframe.
+    pub frame_id: FrameId,
     /// The old pipeline ID for this iframe, if a page was previously loaded.
     pub old_pipeline_id: Option<PipelineId>,
     /// The new pipeline ID that the iframe has generated.
     pub new_pipeline_id: PipelineId,
     /// Sandbox type of this iframe
     pub sandbox: IFrameSandboxState,
     ///  Whether this iframe should be considered private
     pub is_private: bool,
--- a/servo/tests/unit/net/fetch.rs
+++ b/servo/tests/unit/net/fetch.rs
@@ -14,17 +14,17 @@ use hyper::header::{CacheControl, Conten
 use hyper::header::{Encoding, Location, Pragma, SetCookie, qitem};
 use hyper::header::{Headers, Host, HttpDate, Referer as HyperReferer};
 use hyper::method::Method;
 use hyper::mime::{Mime, SubLevel, TopLevel};
 use hyper::server::{Handler, Listening, Server};
 use hyper::server::{Request as HyperRequest, Response as HyperResponse};
 use hyper::status::StatusCode;
 use hyper::uri::RequestUri;
-use msg::constellation_msg::{PipelineId, ReferrerPolicy};
+use msg::constellation_msg::{ReferrerPolicy, TEST_PIPELINE_ID};
 use net::fetch::cors_cache::CORSCache;
 use net::fetch::methods::{FetchContext, fetch, fetch_with_cors_cache};
 use net::http_loader::HttpState;
 use net_traits::FetchTaskTarget;
 use net_traits::request::{Origin, RedirectMode, Referrer, Request, RequestMode};
 use net_traits::response::{CacheState, Response, ResponseBody, ResponseType};
 use std::fs::File;
 use std::io::Read;
@@ -771,18 +771,17 @@ fn test_fetch_with_devtools() {
     static MESSAGE: &'static [u8] = b"Yay!";
     let handler = move |_: HyperRequest, response: HyperResponse| {
         response.send(MESSAGE).unwrap();
     };
 
     let (mut server, url) = make_server(handler);
 
     let origin = Origin::Origin(url.origin());
-    let pipeline_id = PipelineId::fake_root_pipeline_id();
-    let request = Request::new(url.clone(), Some(origin), false, Some(pipeline_id));
+    let request = Request::new(url.clone(), Some(origin), false, Some(TEST_PIPELINE_ID));
     *request.referrer.borrow_mut() = Referrer::NoReferrer;
 
     let (devtools_chan, devtools_port) = channel::<DevtoolsControlMsg>();
 
     let _ = fetch_sync(request, Some(devtools_chan));
     let _ = server.close();
 
     // notification received from devtools
@@ -810,31 +809,31 @@ fn test_fetch_with_devtools() {
 
     headers.set(UserAgent(DEFAULT_USER_AGENT.to_owned()));
 
     let httprequest = DevtoolsHttpRequest {
         url: url,
         method: Method::Get,
         headers: headers,
         body: None,
-        pipeline_id: pipeline_id,
+        pipeline_id: TEST_PIPELINE_ID,
         startedDateTime: devhttprequest.startedDateTime,
         timeStamp: devhttprequest.timeStamp,
         connect_time: devhttprequest.connect_time,
         send_time: devhttprequest.send_time,
         is_xhr: true,
     };
 
     let content = "Yay!";
     let mut response_headers = Headers::new();
     response_headers.set(ContentLength(content.len() as u64));
     devhttpresponse.headers.as_mut().unwrap().remove::<Date>();
 
     let httpresponse = DevtoolsHttpResponse {
         headers: Some(response_headers),
         status: Some((200, b"OK".to_vec())),
         body: None,
-        pipeline_id: pipeline_id,
+        pipeline_id: TEST_PIPELINE_ID,
     };
 
     assert_eq!(devhttprequest, httprequest);
     assert_eq!(devhttpresponse, httpresponse);
 }
--- a/servo/tests/unit/net/http_loader.rs
+++ b/servo/tests/unit/net/http_loader.rs
@@ -13,17 +13,17 @@ use hyper::LanguageTag;
 use hyper::header::{Accept, AcceptEncoding, ContentEncoding, ContentLength, Cookie as CookieHeader};
 use hyper::header::{AcceptLanguage, Authorization, Basic};
 use hyper::header::{Encoding, Headers, Host, Location, Quality, QualityItem, Referer, SetCookie, qitem};
 use hyper::header::{StrictTransportSecurity, UserAgent};
 use hyper::http::RawStatus;
 use hyper::method::Method;
 use hyper::mime::{Mime, SubLevel, TopLevel};
 use hyper::status::StatusCode;
-use msg::constellation_msg::{PipelineId, ReferrerPolicy};
+use msg::constellation_msg::{PipelineId, ReferrerPolicy, TEST_PIPELINE_ID};
 use net::cookie::Cookie;
 use net::cookie_storage::CookieStorage;
 use net::hsts::HstsEntry;
 use net::http_loader::{HttpRequest, HttpRequestFactory, HttpState, LoadError, UIProvider, load};
 use net::http_loader::{HttpResponse, LoadErrorType};
 use net::resource_thread::{AuthCacheEntry, CancellationListener};
 use net_traits::{CookieSource, IncludeSubdomains, LoadContext, LoadData};
 use net_traits::{CustomResponse, LoadOrigin, Metadata};
@@ -42,17 +42,17 @@ struct HttpTest;
 impl LoadOrigin for HttpTest {
     fn referrer_url(&self) -> Option<Url> {
         None
     }
     fn referrer_policy(&self) -> Option<ReferrerPolicy> {
         None
     }
     fn pipeline_id(&self) -> Option<PipelineId> {
-        Some(PipelineId::fake_root_pipeline_id())
+        Some(TEST_PIPELINE_ID)
     }
 }
 
 struct LoadOriginInfo<'a> {
     referrer_url: &'a str,
     referrer_policy: Option<ReferrerPolicy>,
 }
 
@@ -467,18 +467,16 @@ fn test_request_and_response_data_with_n
         }
     }
 
     let http_state = HttpState::new();
     let ui_provider = TestProvider::new();
 
     let url = Url::parse("https://mozilla.com").unwrap();
     let (devtools_chan, devtools_port) = mpsc::channel::<DevtoolsControlMsg>();
-    // This will probably have to be changed as it uses fake_root_pipeline_id which is marked for removal.
-    let pipeline_id = PipelineId::fake_root_pipeline_id();
     let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest);
     let mut request_headers = Headers::new();
     request_headers.set(Host { hostname: "bar.foo".to_owned(), port: None });
     load_data.headers = request_headers.clone();
     let _ = load(&load_data, &ui_provider, &http_state, Some(devtools_chan), &Factory,
                  DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None);
 
     // notification received from devtools
@@ -516,34 +514,34 @@ fn test_request_and_response_data_with_n
 
     headers.set(UserAgent(DEFAULT_USER_AGENT.to_owned()));
 
     let httprequest = DevtoolsHttpRequest {
         url: url,
         method: Method::Get,
         headers: headers,
         body: None,
-        pipeline_id: pipeline_id,
+        pipeline_id: TEST_PIPELINE_ID,
         startedDateTime: devhttprequest.startedDateTime,
         timeStamp: devhttprequest.timeStamp,
         connect_time: devhttprequest.connect_time,
         send_time: devhttprequest.send_time,
         is_xhr: false,
     };
 
     let content = "Yay!";
     let mut response_headers = Headers::new();
     response_headers.set(ContentLength(content.len() as u64));
     response_headers.set(Host { hostname: "foo.bar".to_owned(), port: None });
 
     let httpresponse = DevtoolsHttpResponse {
         headers: Some(response_headers),
         status: Some((200, b"OK".to_vec())),
         body: None,
-        pipeline_id: pipeline_id,
+        pipeline_id: TEST_PIPELINE_ID,
     };
 
     assert_eq!(devhttprequest, httprequest);
     assert_eq!(devhttpresponse, httpresponse);
 }
 
 struct HttpTestNoPipeline;
 impl LoadOrigin for HttpTestNoPipeline {