diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index dc28056e81b..291ed48dd1b 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -221,14 +221,16 @@ pub struct InitialConstellationState { /// Stores the navigation context for a single frame in the frame tree. struct Frame { + id: FrameId, prev: Vec<(PipelineId, Instant)>, current: (PipelineId, Instant), next: Vec<(PipelineId, Instant)>, } impl Frame { - fn new(pipeline_id: PipelineId) -> Frame { + fn new(id: FrameId, pipeline_id: PipelineId) -> Frame { Frame { + id: id, prev: vec!(), current: (pipeline_id, Instant::now()), next: vec!(), @@ -290,6 +292,37 @@ impl<'a> Iterator for FrameTreeIterator<'a> { } } +struct FullFrameTreeIterator<'a> { + stack: Vec, + frames: &'a HashMap, + pipelines: &'a HashMap, +} + +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() { + Some(frame_id) => frame_id, + None => return None, + }; + let frame = match self.frames.get(&frame_id) { + Some(frame) => frame, + None => { + warn!("Frame {:?} iterated after closure.", frame_id); + continue; + }, + }; + for &(pipeline_id, _) in frame.prev.iter().chain(frame.next.iter()).chain(once(&frame.current)) { + if let Some(pipeline) = self.pipelines.get(&pipeline_id) { + self.stack.extend(pipeline.children.iter().map(|&c| c)); + } + } + return Some(frame) + } + } +} + struct WebDriverData { load_channel: Option<(PipelineId, IpcSender)>, resize_channel: Option>, @@ -578,77 +611,46 @@ impl Constellation } } + fn full_frame_tree_iter(&self, frame_id_root: FrameId) -> FullFrameTreeIterator { + FullFrameTreeIterator { + stack: vec!(frame_id_root), + pipelines: &self.pipelines, + frames: &self.frames, + } + } + fn joint_session_future(&self, frame_id_root: FrameId) -> Vec<(Instant, FrameId, PipelineId)> { let mut future = vec!(); - self.get_future_entries(frame_id_root, &mut future); + for frame in self.full_frame_tree_iter(frame_id_root) { + future.extend(frame.next.iter().map(|&(pipeline_id, instant)| (instant, frame.id, pipeline_id))); + } // reverse sorting future.sort_by(|a, b| b.cmp(a)); future } - fn get_future_entries(&self, frame_id_root: FrameId, mut future: &mut Vec<(Instant, FrameId, PipelineId)>) { - let frame = match self.frames.get(&frame_id_root) { - Some(frame) => frame, - None => return warn!("Tried to get frame future after frame {:?} closed.", frame_id_root), - }; - - future.extend(frame.next.iter().map(|&(pipeline_id, instant)| (instant, frame_id_root, pipeline_id))); - - for &(pipeline_id, _) in frame.next.iter().chain(once(&frame.current)) { - let pipeline = match self.pipelines.get(&pipeline_id) { - Some(pipeline) => pipeline, - None => { - warn!("Tried to get pipeline {:?} for child lookup after closure.", pipeline_id); - continue; - } - }; - for &frame_id in &pipeline.children { - self.get_future_entries(frame_id, &mut future); - } - } - } - fn joint_session_past(&self, frame_id_root: FrameId) -> Vec<(Instant, FrameId, PipelineId)> { let mut past = vec!(); - self.get_past_entries(frame_id_root, &mut past); + for frame in self.full_frame_tree_iter(frame_id_root) { + let mut prev_instant = frame.current.1; + for &(pipeline_id, instant) in frame.prev.iter().rev() { + past.push((prev_instant, frame.id, pipeline_id)); + prev_instant = instant; + } + } past.sort(); past } - fn get_past_entries(&self, frame_id_root: FrameId, mut past: &mut Vec<(Instant, FrameId, PipelineId)>) { - let frame = match self.frames.get(&frame_id_root) { - Some(frame) => frame, - None => return warn!("Tried to get frame past after frame {:?} closed.", frame_id_root), - }; - - let mut prev_instant = frame.current.1; - for &(pipeline_id, instant) in frame.prev.iter().rev() { - past.push((prev_instant, frame_id_root, pipeline_id)); - prev_instant = instant; - } - for &(pipeline_id, _) in frame.prev.iter().chain(once(&frame.current)) { - let pipeline = match self.pipelines.get(&pipeline_id) { - Some(pipeline) => pipeline, - None => { - warn!("Tried to get pipeline {:?} for child lookup after closure.", pipeline_id); - continue; - } - }; - for frame_id in &pipeline.children { - self.get_past_entries(*frame_id, &mut 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(pipeline_id); + let frame = Frame::new(id, pipeline_id); assert!(self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.frame).is_none()); assert!(!self.frames.contains_key(&id)); @@ -831,6 +833,11 @@ impl Constellation debug!("constellation got traverse history message from script"); self.handle_traverse_history_msg(pipeline_id, direction); } + // Handle a joint session history length request. + FromScriptMsg::JointSessionHistoryLength(pipeline_id, sender) => { + debug!("constellation got joint session history length message from script"); + self.handle_joint_session_history_length(pipeline_id, sender); + } // Notification that the new document is ready to become active FromScriptMsg::ActivateDocument(pipeline_id) => { debug!("constellation got activate document message"); @@ -1485,6 +1492,25 @@ impl Constellation } } + fn handle_joint_session_history_length(&self, pipeline_id: PipelineId, sender: IpcSender) { + 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; + }, + }; + + // 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, 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 diff --git a/components/script/dom/history.rs b/components/script/dom/history.rs index 061d7616f4a..559b39f9b2c 100644 --- a/components/script/dom/history.rs +++ b/components/script/dom/history.rs @@ -10,6 +10,7 @@ use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::window::Window; +use ipc_channel::ipc; use msg::constellation_msg::TraversalDirection; use script_traits::ScriptMsg as ConstellationMsg; @@ -44,6 +45,15 @@ impl History { } impl HistoryMethods for History { + // https://html.spec.whatwg.org/multipage/#dom-history-length + fn Length(&self) -> u32 { + let pipeline = self.window.pipeline(); + let (sender, recv) = ipc::channel().expect("Failed to create channel to send jsh length."); + let msg = ConstellationMsg::JointSessionHistoryLength(pipeline, sender); + let _ = self.window.constellation_chan().send(msg); + recv.recv().unwrap() + } + // https://html.spec.whatwg.org/multipage/#dom-history-go fn Go(&self, delta: i32) { let direction = if delta > 0 { diff --git a/components/script/dom/webidls/History.webidl b/components/script/dom/webidls/History.webidl index 04742d52601..c0c1635264a 100644 --- a/components/script/dom/webidls/History.webidl +++ b/components/script/dom/webidls/History.webidl @@ -7,7 +7,7 @@ // https://html.spec.whatwg.org/multipage/#the-history-interface [Exposed=(Window,Worker)] interface History { - // readonly attribute unsigned long length; + readonly attribute unsigned long length; // attribute ScrollRestoration scrollRestoration; // readonly attribute any state; void go(optional long delta = 0); diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 58a49165448..c39aec161da 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -91,6 +91,8 @@ pub enum ScriptMsg { MozBrowserEvent(PipelineId, Option, MozBrowserEvent), /// HTMLIFrameElement Forward or Back traversal. TraverseHistory(Option, TraversalDirection), + /// Gets the length of the joint session history from the constellation. + JointSessionHistoryLength(PipelineId, IpcSender), /// Favicon detected NewFavicon(Url), /// Status message to be displayed in the chrome, eg. a link URL on mouseover. diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/001.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/001.html.ini index 46c10baad28..a6dd33e8380 100644 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/001.html.ini +++ b/tests/wpt/metadata/html/browsers/history/the-history-interface/001.html.ini @@ -1,8 +1,5 @@ [001.html] type: testharness - [history.length should update when loading pages in an iframe] - expected: FAIL - [history.length should update when setting location.hash] expected: FAIL diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/002.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/002.html.ini index 1228df18521..6fec825a245 100644 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/002.html.ini +++ b/tests/wpt/metadata/html/browsers/history/the-history-interface/002.html.ini @@ -1,8 +1,5 @@ [002.html] type: testharness - [history.length should update when loading pages in an iframe] - expected: FAIL - [history.length should update when setting location.hash] expected: FAIL diff --git a/tests/wpt/metadata/html/browsers/history/the-location-interface/location_assign_about_blank.html.ini b/tests/wpt/metadata/html/browsers/history/the-location-interface/location_assign_about_blank.html.ini new file mode 100644 index 00000000000..c144ee2f49f --- /dev/null +++ b/tests/wpt/metadata/html/browsers/history/the-location-interface/location_assign_about_blank.html.ini @@ -0,0 +1,5 @@ +[location_assign_about_blank.html] + type: testharness + [location.assign with initial about:blank browsing context] + expected: FAIL + diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index 9cf7bd0c315..9934d8567d6 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -5598,9 +5598,6 @@ [BarProp interface: attribute visible] expected: FAIL - [History interface: attribute length] - expected: FAIL - [History interface: attribute state] expected: FAIL @@ -5610,9 +5607,6 @@ [History interface: operation replaceState(any,DOMString,DOMString)] expected: FAIL - [History interface: window.history must inherit property "length" with the proper type (0)] - expected: FAIL - [History interface: window.history must inherit property "state" with the proper type (1)] expected: FAIL