mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
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:
commit
7aafc0d0ec
8 changed files with 95 additions and 64 deletions
|
@ -221,14 +221,16 @@ pub struct InitialConstellationState {
|
||||||
|
|
||||||
/// Stores the navigation context for a single frame in the frame tree.
|
/// Stores the navigation context for a single frame in the frame tree.
|
||||||
struct Frame {
|
struct Frame {
|
||||||
|
id: FrameId,
|
||||||
prev: Vec<(PipelineId, Instant)>,
|
prev: Vec<(PipelineId, Instant)>,
|
||||||
current: (PipelineId, Instant),
|
current: (PipelineId, Instant),
|
||||||
next: Vec<(PipelineId, Instant)>,
|
next: Vec<(PipelineId, Instant)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Frame {
|
impl Frame {
|
||||||
fn new(pipeline_id: PipelineId) -> Frame {
|
fn new(id: FrameId, pipeline_id: PipelineId) -> Frame {
|
||||||
Frame {
|
Frame {
|
||||||
|
id: id,
|
||||||
prev: vec!(),
|
prev: vec!(),
|
||||||
current: (pipeline_id, Instant::now()),
|
current: (pipeline_id, Instant::now()),
|
||||||
next: vec!(),
|
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 {
|
struct WebDriverData {
|
||||||
load_channel: Option<(PipelineId, IpcSender<webdriver_msg::LoadStatus>)>,
|
load_channel: Option<(PipelineId, IpcSender<webdriver_msg::LoadStatus>)>,
|
||||||
resize_channel: Option<IpcSender<WindowSizeData>>,
|
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)> {
|
fn joint_session_future(&self, frame_id_root: FrameId) -> Vec<(Instant, FrameId, PipelineId)> {
|
||||||
let mut future = vec!();
|
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
|
// reverse sorting
|
||||||
future.sort_by(|a, b| b.cmp(a));
|
future.sort_by(|a, b| b.cmp(a));
|
||||||
future
|
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)> {
|
fn joint_session_past(&self, frame_id_root: FrameId) -> Vec<(Instant, FrameId, PipelineId)> {
|
||||||
let mut past = vec!();
|
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.sort();
|
||||||
past
|
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.
|
// Create a new frame and update the internal bookkeeping.
|
||||||
fn new_frame(&mut self, pipeline_id: PipelineId) -> FrameId {
|
fn new_frame(&mut self, pipeline_id: PipelineId) -> FrameId {
|
||||||
let id = self.next_frame_id;
|
let id = self.next_frame_id;
|
||||||
let FrameId(ref mut i) = self.next_frame_id;
|
let FrameId(ref mut i) = self.next_frame_id;
|
||||||
*i += 1;
|
*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.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.frame).is_none());
|
||||||
assert!(!self.frames.contains_key(&id));
|
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");
|
debug!("constellation got traverse history message from script");
|
||||||
self.handle_traverse_history_msg(pipeline_id, direction);
|
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
|
// Notification that the new document is ready to become active
|
||||||
FromScriptMsg::ActivateDocument(pipeline_id) => {
|
FromScriptMsg::ActivateDocument(pipeline_id) => {
|
||||||
debug!("constellation got activate document message");
|
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) {
|
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
|
// Send to the explicitly focused pipeline (if it exists), or the root
|
||||||
// frame's current pipeline. If neither exist, fall back to sending to
|
// frame's current pipeline. If neither exist, fall back to sending to
|
||||||
|
|
|
@ -10,6 +10,7 @@ use dom::bindings::global::GlobalRef;
|
||||||
use dom::bindings::js::{JS, Root};
|
use dom::bindings::js::{JS, Root};
|
||||||
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
|
use ipc_channel::ipc;
|
||||||
use msg::constellation_msg::TraversalDirection;
|
use msg::constellation_msg::TraversalDirection;
|
||||||
use script_traits::ScriptMsg as ConstellationMsg;
|
use script_traits::ScriptMsg as ConstellationMsg;
|
||||||
|
|
||||||
|
@ -44,6 +45,15 @@ impl History {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HistoryMethods for 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
|
// https://html.spec.whatwg.org/multipage/#dom-history-go
|
||||||
fn Go(&self, delta: i32) {
|
fn Go(&self, delta: i32) {
|
||||||
let direction = if delta > 0 {
|
let direction = if delta > 0 {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
// https://html.spec.whatwg.org/multipage/#the-history-interface
|
// https://html.spec.whatwg.org/multipage/#the-history-interface
|
||||||
[Exposed=(Window,Worker)]
|
[Exposed=(Window,Worker)]
|
||||||
interface History {
|
interface History {
|
||||||
// readonly attribute unsigned long length;
|
readonly attribute unsigned long length;
|
||||||
// attribute ScrollRestoration scrollRestoration;
|
// attribute ScrollRestoration scrollRestoration;
|
||||||
// readonly attribute any state;
|
// readonly attribute any state;
|
||||||
void go(optional long delta = 0);
|
void go(optional long delta = 0);
|
||||||
|
|
|
@ -91,6 +91,8 @@ pub enum ScriptMsg {
|
||||||
MozBrowserEvent(PipelineId, Option<SubpageId>, MozBrowserEvent),
|
MozBrowserEvent(PipelineId, Option<SubpageId>, MozBrowserEvent),
|
||||||
/// HTMLIFrameElement Forward or Back traversal.
|
/// HTMLIFrameElement Forward or Back traversal.
|
||||||
TraverseHistory(Option<PipelineId>, TraversalDirection),
|
TraverseHistory(Option<PipelineId>, TraversalDirection),
|
||||||
|
/// Gets the length of the joint session history from the constellation.
|
||||||
|
JointSessionHistoryLength(PipelineId, IpcSender<u32>),
|
||||||
/// Favicon detected
|
/// Favicon detected
|
||||||
NewFavicon(Url),
|
NewFavicon(Url),
|
||||||
/// Status message to be displayed in the chrome, eg. a link URL on mouseover.
|
/// Status message to be displayed in the chrome, eg. a link URL on mouseover.
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
[001.html]
|
[001.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[history.length should update when loading pages in an iframe]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[history.length should update when setting location.hash]
|
[history.length should update when setting location.hash]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
[002.html]
|
[002.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[history.length should update when loading pages in an iframe]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[history.length should update when setting location.hash]
|
[history.length should update when setting location.hash]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
[location_assign_about_blank.html]
|
||||||
|
type: testharness
|
||||||
|
[location.assign with initial about:blank browsing context]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -5598,9 +5598,6 @@
|
||||||
[BarProp interface: attribute visible]
|
[BarProp interface: attribute visible]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[History interface: attribute length]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[History interface: attribute state]
|
[History interface: attribute state]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -5610,9 +5607,6 @@
|
||||||
[History interface: operation replaceState(any,DOMString,DOMString)]
|
[History interface: operation replaceState(any,DOMString,DOMString)]
|
||||||
expected: FAIL
|
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)]
|
[History interface: window.history must inherit property "state" with the proper type (1)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue