Auto merge of #12685 - ConnorGBrewster:history_length, r=asajeffrey

Implement history.length

<!-- Please describe your changes on the following line: -->

---
<!-- 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
- [ ] These changes fix #__ (github issue number if applicable).

<!-- Either: -->
- [x] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/12685)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-08-03 22:22:57 -05:00 committed by GitHub
commit 7aafc0d0ec
8 changed files with 95 additions and 64 deletions

View file

@ -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<FrameId>,
frames: &'a HashMap<FrameId, Frame>,
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() {
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<webdriver_msg::LoadStatus>)>,
resize_channel: Option<IpcSender<WindowSizeData>>,
@ -578,77 +611,46 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
}
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<Message, LTF, STF> Constellation<Message, LTF, STF>
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<Message, LTF, STF> Constellation<Message, LTF, STF>
}
}
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;
},
};
// 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

View file

@ -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 {

View file

@ -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);

View file

@ -91,6 +91,8 @@ pub enum ScriptMsg {
MozBrowserEvent(PipelineId, Option<SubpageId>, MozBrowserEvent),
/// HTMLIFrameElement Forward or Back traversal.
TraverseHistory(Option<PipelineId>, TraversalDirection),
/// Gets the length of the joint session history from the constellation.
JointSessionHistoryLength(PipelineId, IpcSender<u32>),
/// Favicon detected
NewFavicon(Url),
/// Status message to be displayed in the chrome, eg. a link URL on mouseover.

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,5 @@
[location_assign_about_blank.html]
type: testharness
[location.assign with initial about:blank browsing context]
expected: FAIL

View file

@ -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