diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 15c0e258ea3c..13e3deb1ae5a 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -82,7 +82,7 @@ use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use layout_traits::LayoutThreadFactory; use log::{Log, LogLevel, LogLevelFilter, LogMetadata, LogRecord}; -use msg::constellation_msg::{FrameId, FrameType, PipelineId}; +use msg::constellation_msg::{FrameId, FrameType, HistoryStateId, PipelineId}; use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection}; use net_traits::{self, IpcSend, ResourceThreads}; @@ -940,6 +940,11 @@ impl Constellation debug!("constellation got joint session history length message from script"); self.handle_joint_session_history_length(pipeline_id, sender); } + // Handle a history state pushed request. + FromScriptMsg::HistoryStatePushed(pipeline_id, state_id) => { + debug!("constellation got histort state pushed message from script"); + self.handle_history_state_pushed(pipeline_id, state_id); + } // Notification that the new document is ready to become active FromScriptMsg::ActivateDocument(pipeline_id) => { debug!("constellation got activate document message"); @@ -1713,6 +1718,21 @@ impl Constellation let _ = sender.send(length as u32); } + fn handle_history_state_pushed(&mut self, pipeline_id: PipelineId, state_id: HistoryStateId) { + if !self.pipeline_is_in_current_frame(pipeline_id) { + return warn!("Received push state from not fully active pipeline {:?}", pipeline_id); + } + let frame_id = match self.pipelines.get(&pipeline_id) { + Some(pipeline) => pipeline.frame_id, + None => return warn!("Pipeline closed after history state was pushed."), + }; + let frame = match self.frames.get_mut(&frame_id) { + Some(frame) => frame, + None => return warn!("Frame closed after history state was pushed."), + }; + frame.push_state(state_id); + } + 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 @@ -2033,9 +2053,7 @@ impl Constellation debug_assert_eq!(entry.instant, curr_entry.instant); - frame.pipeline_id = pipeline_id; - frame.instant = entry.instant; - frame.url = entry.url.clone(); + frame.update_current(pipeline_id, &entry); old_pipeline_id }, @@ -2063,6 +2081,16 @@ impl Constellation // Set paint permissions correctly for the compositor layers. self.send_frame_tree(); + // Notify the corresponding browsing context to activate the specified history state. + match self.pipelines.get(&pipeline_id) { + Some(pipeline) => { + let msg = ConstellationControlMsg::ActivateHistoryState(pipeline_id, + entry.state_id); + let _ = pipeline.event_loop.send(msg); + }, + None => return warn!("Pipeline {:?} traversed after closure.", pipeline_id), + }; + // Update the owning iframe to point to the new pipeline id. // This makes things like contentDocument work correctly. if let Some((parent_pipeline_id, _)) = parent_info { @@ -2130,7 +2158,7 @@ impl Constellation let (evicted_id, new_frame, clear_future, location_changed) = if let Some(mut entry) = frame_change.replace { debug!("Replacing pipeline in existing frame."); let evicted_id = entry.pipeline_id; - entry.pipeline_id = Some(frame_change.new_pipeline_id); + entry.replace_pipeline(frame_change.new_pipeline_id, frame_change.url.clone()); self.traverse_to_entry(entry); (evicted_id, false, false, false) } else if let Some(frame) = self.frames.get_mut(&frame_change.frame_id) { @@ -2366,13 +2394,31 @@ impl Constellation .map(|frame| frame.id) .collect(); for frame_id in frame_ids { - let evicted = match self.frames.get_mut(&frame_id) { - Some(frame) => frame.remove_forward_entries(), + let (current_pipeline_id, forward_entries) = match self.frames.get_mut(&frame_id) { + Some(frame) => (frame.pipeline_id, frame.remove_forward_entries()), None => continue, }; - for entry in evicted { + let mut discarded_history_states = vec!(); + for entry in forward_entries { if let Some(pipeline_id) = entry.pipeline_id { - self.close_pipeline(pipeline_id, DiscardBrowsingContext::No, ExitPipelineMode::Normal); + if pipeline_id == current_pipeline_id { + discarded_history_states.push(entry.state_id); + } else { + self.close_pipeline(pipeline_id, DiscardBrowsingContext::No, ExitPipelineMode::Normal); + } + } + } + + // Notify the BrowsingContext in the script thread to remove the HistoryState entries + // that are now no longer accesible. + if !discarded_history_states.is_empty() { + match self.pipelines.get(¤t_pipeline_id) { + Some(pipeline) => { + let msg = ConstellationControlMsg::RemoveHistoryStateEntries(current_pipeline_id, + discarded_history_states); + let _ = pipeline.event_loop.send(msg); + }, + None => return warn!("Pipeline {:?} closed after removing state entries", current_pipeline_id), } } } diff --git a/components/constellation/frame.rs b/components/constellation/frame.rs index 1613e18b6702..c6961822f869 100644 --- a/components/constellation/frame.rs +++ b/components/constellation/frame.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use msg::constellation_msg::{FrameId, PipelineId}; +use msg::constellation_msg::{FrameId, HistoryStateId, PipelineId}; use pipeline::Pipeline; use servo_url::ServoUrl; use std::collections::HashMap; @@ -29,6 +29,9 @@ pub struct Frame { /// The pipeline for the current session history entry pub pipeline_id: PipelineId, + /// The id that maps to the history state entry in the script thread + pub state_id: HistoryStateId, + /// The URL for the current session history entry pub url: ServoUrl, @@ -45,8 +48,9 @@ impl Frame { pub fn new(id: FrameId, pipeline_id: PipelineId, url: ServoUrl) -> Frame { Frame { id: id, - pipeline_id: pipeline_id, instant: Instant::now(), + pipeline_id: pipeline_id, + state_id: HistoryStateId(0), url: url, prev: vec!(), next: vec!(), @@ -57,9 +61,10 @@ impl Frame { pub fn current(&self) -> FrameState { FrameState { instant: self.instant, - frame_id: self.id, pipeline_id: Some(self.pipeline_id), url: self.url.clone(), + frame_id: self.id, + state_id: self.state_id, } } @@ -70,12 +75,29 @@ impl Frame { self.instant = Instant::now(); self.pipeline_id = pipeline_id; self.url = url; + self.state_id = HistoryStateId(0); } + /// Add an entry representing a new history state. + pub fn push_state(&mut self, state_id: HistoryStateId) { + let current = self.current(); + self.prev.push(current); + // Only update the time stampt and history state + self.instant = Instant::now(); + self.state_id = state_id; + } /// Set the future to be empty. pub fn remove_forward_entries(&mut self) -> Vec { replace(&mut self.next, vec!()) } + + /// Update the current entry of the Frame from an entry that has been traversed to. + pub fn update_current(&mut self, pipeline_id: PipelineId, entry: &FrameState) { + self.pipeline_id = pipeline_id; + self.instant = entry.instant; + self.url = entry.url.clone(); + self.state_id = entry.state_id; + } } /// An entry in a frame's session history. @@ -97,6 +119,19 @@ pub struct FrameState { /// The frame that this session history entry is part of pub frame_id: FrameId, + + /// The id that maps to the history state entry in the script thread + pub state_id: HistoryStateId, +} + +impl FrameState { + /// Updates the entry's pipeline and url. This is used when navigating with replacement + /// enabled. + pub fn replace_pipeline(&mut self, pipeline_id: PipelineId, url: ServoUrl) { + self.pipeline_id = Some(pipeline_id); + self.url = url; + self.state_id = HistoryStateId(0); + } } /// Represents a pending change in the frame tree, that will be applied @@ -197,11 +232,19 @@ impl<'a> Iterator for FullFrameTreeIterator<'a> { continue; }, }; - let child_frame_ids = frame.prev.iter().chain(frame.next.iter()) + let mut session_pipelines = frame.prev.iter().chain(frame.next.iter()) .filter_map(|entry| entry.pipeline_id) .chain(once(frame.pipeline_id)) + .collect::>(); + + // The session history may contain multiple entries with the same PipelineId. + // These duplicates need to be removed. + session_pipelines.dedup(); + + let child_frame_ids = session_pipelines.iter() .filter_map(|pipeline_id| pipelines.get(&pipeline_id)) .flat_map(|pipeline| pipeline.children.iter()); + self.stack.extend(child_frame_ids); return Some(frame) } diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index dddfb0c00422..51a7bf4458dd 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -217,6 +217,9 @@ impl PipelineNamespace { thread_local!(pub static PIPELINE_NAMESPACE: Cell> = Cell::new(None)); +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] +pub struct HistoryStateId(pub u32); + #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] pub struct PipelineNamespaceId(pub u32); diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 6f211f82bef3..157344760322 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -58,7 +58,7 @@ use js::glue::{CallObjectTracer, CallUnbarrieredObjectTracer, CallValueTracer}; use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind}; use js::jsval::JSVal; use js::rust::Runtime; -use msg::constellation_msg::{FrameId, FrameType, PipelineId}; +use msg::constellation_msg::{FrameId, FrameType, HistoryStateId, PipelineId}; use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads}; use net_traits::filemanager_thread::RelativePos; use net_traits::image::base::{Image, ImageMetadata}; @@ -331,7 +331,7 @@ unsafe_no_jsmanaged_fields!(TrustedPromise); unsafe_no_jsmanaged_fields!(PropertyDeclarationBlock); // These three are interdependent, if you plan to put jsmanaged data // in one of these make sure it is propagated properly to containing structs -unsafe_no_jsmanaged_fields!(FrameId, FrameType, WindowSizeData, WindowSizeType, PipelineId); +unsafe_no_jsmanaged_fields!(FrameId, FrameType, HistoryStateId, WindowSizeData, WindowSizeType, PipelineId); unsafe_no_jsmanaged_fields!(TimerEventId, TimerSource); unsafe_no_jsmanaged_fields!(TimelineMarkerType); unsafe_no_jsmanaged_fields!(WorkerId); diff --git a/components/script/dom/browsingcontext.rs b/components/script/dom/browsingcontext.rs index 1c2ca66b33e1..1156ca4a67f7 100644 --- a/components/script/dom/browsingcontext.rs +++ b/components/script/dom/browsingcontext.rs @@ -2,28 +2,39 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject}; +use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root, RootedReference}; use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor}; use dom::bindings::reflector::{DomObject, MutDomObject, Reflector}; +use dom::bindings::str::DOMString; use dom::bindings::trace::JSTraceable; use dom::bindings::utils::WindowProxyHandler; use dom::bindings::utils::get_array_index_from_id; use dom::element::Element; +use dom::eventtarget::EventTarget; +use dom::globalscope::GlobalScope; +use dom::popstateevent::PopStateEvent; use dom::window::Window; use js::JSCLASS_IS_GLOBAL; use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy}; use js::glue::{GetProxyPrivate, SetProxyExtra, GetProxyExtra}; -use js::jsapi::{Handle, HandleId, HandleObject, HandleValue}; +use js::jsapi::{Handle, HandleId, HandleObject, HandleValue, Heap}; use js::jsapi::{JSAutoCompartment, JSContext, JSErrNum, JSFreeOp, JSObject}; use js::jsapi::{JSPROP_READONLY, JSTracer, JS_DefinePropertyById}; use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo}; use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById}; use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue}; use js::jsapi::{ObjectOpResult, PropertyDescriptor}; -use js::jsval::{UndefinedValue, PrivateValue}; +use js::jsval::{JSVal, PrivateValue, UndefinedValue}; use js::rust::get_object_class; +use msg::constellation_msg::HistoryStateId; +use script_traits::ScriptMsg as ConstellationMsg; +use servo_url::ServoUrl; use std::cell::Cell; +use std::collections::HashMap; #[dom_struct] // NOTE: the browsing context for a window is managed in two places: @@ -33,24 +44,38 @@ use std::cell::Cell; pub struct BrowsingContext { reflector: Reflector, + window: JS, + /// Has this browsing context been discarded? discarded: Cell, + active_state: Cell, + + next_state_id: Cell, + + states: DOMRefCell>, + /// The containing iframe element, if this is a same-origin iframe frame_element: Option>, } impl BrowsingContext { - pub fn new_inherited(frame_element: Option<&Element>) -> BrowsingContext { + pub fn new_inherited(window: &Window, frame_element: Option<&Element>, url: ServoUrl) -> BrowsingContext { + let mut states = HashMap::new(); + states.insert(HistoryStateId(0), HistoryState::new(None, None, url)); BrowsingContext { reflector: Reflector::new(), + window: JS::from_ref(window), discarded: Cell::new(false), + active_state: Cell::new(HistoryStateId(0)), + next_state_id: Cell::new(HistoryStateId(1)), + states: DOMRefCell::new(states), frame_element: frame_element.map(JS::from_ref), } } #[allow(unsafe_code)] - pub fn new(window: &Window, frame_element: Option<&Element>) -> Root { + pub fn new(window: &Window, frame_element: Option<&Element>, url: ServoUrl) -> Root { unsafe { let WindowProxyHandler(handler) = window.windowproxy_handler(); assert!(!handler.is_null()); @@ -63,7 +88,7 @@ impl BrowsingContext { rooted!(in(cx) let window_proxy = NewWindowProxy(cx, parent, handler)); assert!(!window_proxy.is_null()); - let object = box BrowsingContext::new_inherited(frame_element); + let object = box BrowsingContext::new_inherited(window, frame_element, url); let raw = Box::into_raw(object); SetProxyExtra(window_proxy.get(), 0, &PrivateValue(raw as *const _)); @@ -78,10 +103,67 @@ impl BrowsingContext { self.discarded.set(true); } + fn next_state_id(&self) -> HistoryStateId { + let next_id = self.next_state_id.get(); + self.next_state_id.set(HistoryStateId(next_id.0 + 1)); + next_id + } + + // TODO(ConnorGBrewster): Store and do something with `title`, and `url` + pub fn replace_session_history_entry(&self, + title: DOMString, + url: ServoUrl, + state: HandleValue) { + let mut states = self.states.borrow_mut(); + states.insert(self.active_state.get(), HistoryState::new(Some(state), Some(title), url)); + // NOTE: We do not need to notify the constellation, as the history state id + // will stay the same and no new entry is added. + } + + pub fn push_session_history_entry(&self, + title: DOMString, + url: ServoUrl, + state: HandleValue) { + let window = &*self.window; + let next_id = self.next_state_id(); + window.Document().set_url(url.clone()); + self.states.borrow_mut().insert(next_id, HistoryState::new(Some(state), Some(title), url)); + self.active_state.set(next_id); + + // Notify the constellation about this new entry so it can be added to the + // joint session history. + let global_scope = window.upcast::(); + let msg = ConstellationMsg::HistoryStatePushed(global_scope.pipeline_id(), next_id); + let _ = global_scope.constellation_chan().send(msg); + } + + pub fn remove_history_state_entries(&self, state_ids: Vec) { + let mut states = self.states.borrow_mut(); + for state_id in state_ids { + states.remove(&state_id); + } + } + + pub fn activate_history_state(&self, state_id: HistoryStateId) { + let window = &*self.window; + let (handle, url) = { + let states = self.states.borrow(); + let state = states.get(&state_id).expect("Activated nonexistent history state."); + (state.state.handle(), state.url.clone()) + }; + window.Document().set_url(url); + PopStateEvent::dispatch_jsval(window.upcast::(), window.upcast::(), handle); + self.active_state.set(state_id); + } + pub fn is_discarded(&self) -> bool { self.discarded.get() } + pub fn state(&self) -> JSVal { + self.states.borrow().get(&self.active_state.get()).expect("No active state.").state.get() + } + pub fn frame_element(&self) -> Option<&Element> { self.frame_element.r() } @@ -93,6 +175,27 @@ impl BrowsingContext { } } +#[derive(JSTraceable, HeapSizeOf)] +struct HistoryState { + title: Option, + url: ServoUrl, + state: Heap, +} + +impl HistoryState { + fn new(state: Option, title: Option, url: ServoUrl) -> HistoryState { + let mut jsval: Heap = Default::default(); + let state = state.unwrap_or(HandleValue::null()); + jsval.set(state.get()); + HistoryState { + title: title, + url: url, + state: jsval, + } + } +} + + #[allow(unsafe_code)] unsafe fn GetSubframeWindow(cx: *mut JSContext, proxy: HandleObject, diff --git a/components/script/dom/history.rs b/components/script/dom/history.rs index 1a971eae0092..a375d941c0c9 100644 --- a/components/script/dom/history.rs +++ b/components/script/dom/history.rs @@ -6,12 +6,17 @@ use dom::bindings::codegen::Bindings::HistoryBinding; use dom::bindings::codegen::Bindings::HistoryBinding::HistoryMethods; use dom::bindings::codegen::Bindings::LocationBinding::LocationMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; +use dom::bindings::error::{Error, ErrorResult}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::str::{DOMString, USVString}; +use dom::bindings::structuredclone::StructuredCloneData; use dom::globalscope::GlobalScope; use dom::window::Window; use ipc_channel::ipc; +use js::jsapi::{HandleValue, JSContext}; +use js::jsval::{JSVal, NullValue}; use msg::constellation_msg::TraversalDirection; use script_traits::ScriptMsg as ConstellationMsg; @@ -35,6 +40,65 @@ impl History { window, HistoryBinding::Wrap) } + + // https://html.spec.whatwg.org/multipage/#dom-history-pushstate + fn add_state(&self, + cx: *mut JSContext, + data: HandleValue, + title: DOMString, + url: Option, + replace: bool) -> ErrorResult { + // Step 1 + let document = self.window.Document(); + // Step 2 + if !document.is_fully_active() { + return Err(Error::Security); + } + // Step 5 + let cloned_data = try!(StructuredCloneData::write(cx, data)); + rooted!(in(cx) let mut state = NullValue()); + cloned_data.read(self.window.upcast::(), state.handle_mut()); + let url = match url { + Some(url) => { + // Step 6 + let document_url = document.url(); + // 6.1 + let url = match document_url.join(&url.0) { + Ok(url) => url, + // 6.2 + Err(_) => return Err(Error::Security), + }; + + // 6.4 + if url.scheme() != document_url.scheme() || + url.username() != document_url.username() || + url.password() != document_url.password() || + url.host() != document_url.host() || + url.port() != document_url.port() { + return Err(Error::Security); + } + + // 6.5 + if url.origin() != document_url.origin() && + (url.path() != document_url.path() || url.query() != document_url.query()) { + return Err(Error::Security); + } + + url + }, + // Step 7 + None => document.url().clone(), + }; + + // Step 8 + if replace { + self.window.browsing_context().replace_session_history_entry(title, url, state.handle()); + } else { + self.window.browsing_context().push_session_history_entry(title, url, state.handle()); + } + + Ok(()) + } } impl History { @@ -57,6 +121,12 @@ impl HistoryMethods for History { recv.recv().unwrap() } + #[allow(unsafe_code)] + // https://html.spec.whatwg.org/multipage/#dom-history-state + unsafe fn State(&self, _cx: *mut JSContext) -> JSVal { + self.window.browsing_context().state() + } + // https://html.spec.whatwg.org/multipage/#dom-history-go fn Go(&self, delta: i32) { let direction = if delta > 0 { @@ -80,4 +150,24 @@ impl HistoryMethods for History { fn Forward(&self) { self.traverse_history(TraversalDirection::Forward(1)); } + + #[allow(unsafe_code)] + // https://html.spec.whatwg.org/multipage/#dom-history-pushstate + unsafe fn PushState(&self, + cx: *mut JSContext, + data: HandleValue, + title: DOMString, + url: Option) -> ErrorResult { + self.add_state(cx, data, title, url, false) + } + + #[allow(unsafe_code)] + // https://html.spec.whatwg.org/multipage/#dom-history-replacestate + unsafe fn ReplaceState(&self, + cx: *mut JSContext, + data: HandleValue, + title: DOMString, + url: Option) -> ErrorResult { + self.add_state(cx, data, title, url, true) + } } diff --git a/components/script/dom/popstateevent.rs b/components/script/dom/popstateevent.rs index 44183f7483f4..3df8b3d9424f 100644 --- a/components/script/dom/popstateevent.rs +++ b/components/script/dom/popstateevent.rs @@ -11,6 +11,7 @@ use dom::bindings::js::{MutHeapJSVal, Root}; use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; use dom::event::Event; +use dom::eventtarget::EventTarget; use dom::globalscope::GlobalScope; use js::jsapi::{HandleValue, JSContext}; use js::jsval::JSVal; @@ -66,6 +67,16 @@ impl PopStateEvent { } } +impl PopStateEvent { + pub fn dispatch_jsval(target: &EventTarget, + scope: &GlobalScope, + state: HandleValue) { + let pop_state_event = PopStateEvent::new( + scope, Atom::from("popstate"), true, false, state); + pop_state_event.upcast::().fire(target); + } +} + impl PopStateEventMethods for PopStateEvent { #[allow(unsafe_code)] // https://html.spec.whatwg.org/multipage/#dom-popstateevent-state diff --git a/components/script/dom/webidls/History.webidl b/components/script/dom/webidls/History.webidl index c0c1635264a3..918403b35ad4 100644 --- a/components/script/dom/webidls/History.webidl +++ b/components/script/dom/webidls/History.webidl @@ -9,10 +9,12 @@ interface History { readonly attribute unsigned long length; // attribute ScrollRestoration scrollRestoration; - // readonly attribute any state; + readonly attribute any state; void go(optional long delta = 0); void back(); void forward(); - // void pushState(any data, DOMString title, optional USVString? url = null); - // void replaceState(any data, DOMString title, optional USVString? url = null); + [Throws] + void pushState(any data, DOMString title, optional USVString? url = null); + [Throws] + void replaceState(any data, DOMString title, optional USVString? url = null); }; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 655825710255..b52e3c1828ed 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -70,7 +70,7 @@ use js::jsval::UndefinedValue; use js::rust::Runtime; use layout_wrapper::ServoLayoutNode; use mem::heap_size_of_self_and_children; -use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespace}; +use msg::constellation_msg::{FrameId, FrameType, HistoryStateId, PipelineId, PipelineNamespace}; use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseListener}; use net_traits::{IpcSend, Metadata, ReferrerPolicy, ResourceThreads}; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread}; @@ -1019,6 +1019,10 @@ impl ScriptThread { self.handle_exit_pipeline_msg(pipeline_id, discard_browsing_context), ConstellationControlMsg::WebVREvent(pipeline_id, event) => self.handle_webvr_event(pipeline_id, event), + ConstellationControlMsg::ActivateHistoryState(pipeline_id, state_id) => + self.handle_activate_history_state(pipeline_id, state_id), + ConstellationControlMsg::RemoveHistoryStateEntries(pipeline_id, state_ids) => + self.handle_remove_history_state_entries(pipeline_id, state_ids), msg @ ConstellationControlMsg::AttachLayout(..) | msg @ ConstellationControlMsg::Viewport(..) | msg @ ConstellationControlMsg::SetScrollState(..) | @@ -1760,7 +1764,7 @@ impl ScriptThread { self.webvr_thread.clone()); let frame_element = frame_element.r().map(Castable::upcast); - let browsing_context = BrowsingContext::new(&window, frame_element); + let browsing_context = BrowsingContext::new(&window, frame_element, incomplete.url.clone()); window.init_browsing_context(&browsing_context); let last_modified = metadata.headers.as_ref().and_then(|headers| { @@ -2223,6 +2227,20 @@ impl ScriptThread { } } + fn handle_activate_history_state(&self, pipeline_id: PipelineId, state_id: HistoryStateId) { + if let Some(window) = self.documents.borrow().find_window(pipeline_id) { + let browsing_context = window.browsing_context(); + browsing_context.activate_history_state(state_id); + } + } + + fn handle_remove_history_state_entries(&self, pipeline_id: PipelineId, state_ids: Vec) { + if let Some(window) = self.documents.borrow().find_window(pipeline_id) { + let browsing_context = window.browsing_context(); + browsing_context.remove_history_state_entries(state_ids); + } + } + pub fn enqueue_promise_job(job: EnqueuedPromiseCallback, global: &GlobalScope) { SCRIPT_THREAD_ROOT.with(|root| { let script_thread = unsafe { &*root.get().unwrap() }; diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index c01311583269..bc134fe9ccd9 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -55,7 +55,7 @@ use hyper::header::Headers; use hyper::method::Method; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use libc::c_void; -use msg::constellation_msg::{FrameId, FrameType, Key, KeyModifiers, KeyState}; +use msg::constellation_msg::{FrameId, FrameType, HistoryStateId, Key, KeyModifiers, KeyState}; use msg::constellation_msg::{PipelineId, PipelineNamespaceId, TraversalDirection}; use net_traits::{ReferrerPolicy, ResourceThreads}; use net_traits::image::base::Image; @@ -265,7 +265,11 @@ pub enum ConstellationControlMsg { /// Reload the given page. Reload(PipelineId), /// Notifies the script thread of a WebVR device event - WebVREvent(PipelineId, WebVREventMsg) + WebVREvent(PipelineId, WebVREventMsg), + /// Notifies a browsing context to activate the specified history state. + ActivateHistoryState(PipelineId, HistoryStateId), + /// Notifies a browsing context to remove the specified history state entries. + RemoveHistoryStateEntries(PipelineId, Vec), } impl fmt::Debug for ConstellationControlMsg { @@ -299,6 +303,8 @@ impl fmt::Debug for ConstellationControlMsg { ReportCSSError(..) => "ReportCSSError", Reload(..) => "Reload", WebVREvent(..) => "WebVREvent", + ActivateHistoryState(..) => "ActivateHistoryStateZ", + RemoveHistoryStateEntries(..) => "RemoveHistoryStateEntries", }; write!(formatter, "ConstellationMsg::{}", variant) } diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index fc29563cbbd1..4d55eaab276a 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -19,7 +19,7 @@ use euclid::size::Size2D; use gfx_traits::ScrollRootId; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::{FrameId, PipelineId, TraversalDirection}; -use msg::constellation_msg::{Key, KeyModifiers, KeyState}; +use msg::constellation_msg::{HistoryStateId, Key, KeyModifiers, KeyState}; use net_traits::CoreResourceMsg; use net_traits::storage_thread::StorageType; use offscreen_gl_context::{GLContextAttributes, GLLimits}; @@ -97,6 +97,8 @@ pub enum ScriptMsg { TraverseHistory(Option, TraversalDirection), /// Gets the length of the joint session history from the constellation. JointSessionHistoryLength(PipelineId, IpcSender), + /// Notifies the constellation that a history state entry has been pushed + HistoryStatePushed(PipelineId, HistoryStateId), /// Favicon detected NewFavicon(ServoUrl), /// Status message to be displayed in the chrome, eg. a link URL on mouseover. diff --git a/etc/ci/former_intermittents_wpt.txt b/etc/ci/former_intermittents_wpt.txt index 6001c12b3a23..e877f0433da3 100644 --- a/etc/ci/former_intermittents_wpt.txt +++ b/etc/ci/former_intermittents_wpt.txt @@ -1,2 +1,4 @@ /_mozilla/mozilla/css/canvas_over_area.html /_mozilla/mozilla/css/iframe/hide_layers2.html +/html/browsers/history/the-history-interface/001.html +/html/browsers/history/the-history-interface/002.html 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 86c15b26af03..a30c5885307a 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,48 +1,8 @@ [001.html] type: testharness - disabled: https://github.com/servo/servo/issues/12580 [history.length should update when setting location.hash] expected: FAIL - [history.pushState must exist] - expected: FAIL - - [history.pushState must exist within iframes] - expected: FAIL - - [initial history.state should be null] - expected: FAIL - - [history.length should update when pushing a state] - expected: FAIL - - [history.state should update after a state is pushed] - expected: FAIL - - [traversing history must traverse pushed states] - expected: FAIL - - [pushState must not be allowed to create invalid URLs] - expected: FAIL - - [pushState must not be allowed to create cross-origin URLs] - expected: FAIL - - [pushState must not be allowed to create cross-origin URLs (about:blank)] - expected: FAIL - - [pushState must not be allowed to create cross-origin URLs (data:URI)] - expected: FAIL - - [pushState should not actually load the new URL] - expected: FAIL - - [security errors are expected to be thrown in the context of the document that owns the history object] - expected: FAIL - - [location.hash must be allowed to change (part 1)] - expected: FAIL - [location.hash must be allowed to change (part 2)] expected: FAIL @@ -52,30 +12,6 @@ [pushState must remove all history after the current state] expected: FAIL - [pushState must be able to set location.hash] - expected: FAIL - - [pushState must remove any tasks queued by the history traversal task source] - expected: FAIL - - [pushState must be able to set location.pathname] - expected: FAIL - - [pushState must be able to set absolute URLs to the same host] - expected: FAIL - - [pushState must not be able to use a function as data] - expected: FAIL - - [pushState must not be able to use a DOM node as data] - expected: FAIL - - [pushState must not be able to use an error object as data] - expected: FAIL - - [security errors are expected to be thrown in the context of the document that owns the history object (2)] - expected: FAIL - [pushState must be able to make structured clones of complex objects] expected: FAIL @@ -83,7 +19,8 @@ expected: FAIL [popstate event should fire when navigation occurs] - expected: FAIL + expected: + if debug and (os == "mac") and (version == "OS X 10.12") and (processor == "x86_64") and (bits == 64) and (backend == "webrender"): PASS [popstate event should pass the state data] expected: FAIL @@ -92,7 +29,8 @@ expected: FAIL [state data should be a clone of the original object, not a reference to it] - expected: FAIL + expected: + if debug and (os == "mac") and (version == "OS X 10.12") and (processor == "x86_64") and (bits == 64) and (backend == "webrender"): PASS [history.state should also reference a clone of the original object (2)] 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 c8350da8e2eb..462488d6339c 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,51 +1,17 @@ [002.html] type: testharness - disabled: https://github.com/servo/servo/issues/12580 [history.length should update when setting location.hash] expected: FAIL - [history.replaceState must exist] - expected: FAIL - - [history.replaceState must exist within iframes] - expected: FAIL - - [initial history.state should be null] - expected: FAIL - - [history.length should not update when replacing a state with no URL] - expected: FAIL - - [history.state should update after a state is pushed] - expected: FAIL - [hash should not change when replaceState is called without a URL] expected: FAIL - [history.length should not update when replacing a state with a URL] - expected: FAIL - [hash should change when replaceState is called with a URL] expected: FAIL [replaceState must replace the existing state without altering the forward history] expected: FAIL - [replaceState must not be allowed to create invalid URLs] - expected: FAIL - - [replaceState must not be allowed to create cross-origin URLs] - expected: FAIL - - [replaceState must not be allowed to create cross-origin URLs (about:blank)] - expected: FAIL - - [replaceState must not be allowed to create cross-origin URLs (data:URI)] - expected: FAIL - - [security errors are expected to be thrown in the context of the document that owns the history object] - expected: FAIL - [replaceState must be able to set location.pathname] expected: FAIL @@ -58,42 +24,24 @@ [.go must queue a task with the history traversal task source (run asynchronously)] expected: FAIL - [replaceState must not be able to use a function as data] - expected: FAIL - - [replaceState must not be able to use a DOM node as data] - expected: FAIL - - [replaceState must not be able to use an error object as data] - expected: FAIL - - [replaceState should not actually load the new URL] - expected: FAIL - - [security errors are expected to be thrown in the context of the document that owns the history object (2)] - expected: FAIL - [replaceState must be able to make structured clones of complex objects] expected: FAIL [history.state should also reference a clone of the original object] expected: FAIL - [popstate event should fire when navigation occurs] - expected: FAIL - [popstate event should pass the state data] expected: FAIL [state data should cope with circular object references] expected: FAIL - [state data should be a clone of the original object, not a reference to it] - expected: FAIL - [history.state should also reference a clone of the original object (2)] expected: FAIL [history.state should be a separate clone of the object, not a reference to the object passed to the event handler] expected: FAIL + [replaceState should not actually load the new URL] + expected: FAIL + diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/005.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/005.html.ini index 760996066882..9de54ad21540 100644 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/005.html.ini +++ b/tests/wpt/metadata/html/browsers/history/the-history-interface/005.html.ini @@ -1,5 +1,6 @@ [005.html] type: testharness + expected: TIMEOUT [history.pushState support is needed for this testcase] expected: FAIL diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/006.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/006.html.ini deleted file mode 100644 index 505c9b8cf838..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/006.html.ini +++ /dev/null @@ -1,14 +0,0 @@ -[006.html] - type: testharness - [history.state should initially be null] - expected: FAIL - - [history.state should still be null onload] - expected: FAIL - - [history.state should still be null after onload] - expected: FAIL - - [writing to history.state should be silently ignored and not throw an error] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/007.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/007.html.ini deleted file mode 100644 index 5c3b95e454e7..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/007.html.ini +++ /dev/null @@ -1,23 +0,0 @@ -[007.html] - type: testharness - [history.state should initially be null] - expected: FAIL - - [history.pushState support is needed for this testcase] - expected: FAIL - - [history.state should reflect pushed state] - expected: FAIL - - [popstate event should fire before onload fires] - expected: FAIL - - [the correct state should be restored when navigating during initial load] - expected: FAIL - - [history.state should reflect the navigated state onload] - expected: FAIL - - [history.state should reflect the navigated state after onload] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/008.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/008.html.ini index 5af01f618f1d..b0269b37f5e4 100644 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/008.html.ini +++ b/tests/wpt/metadata/html/browsers/history/the-history-interface/008.html.ini @@ -1,8 +1,5 @@ [008.html] type: testharness - [history.pushState URL resolving should be done relative to the document, not the script] - expected: FAIL - [history.replaceState URL resolving should be done relative to the document, not the script] expected: FAIL diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/009.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/009.html.ini deleted file mode 100644 index 775bd6adf7d8..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/009.html.ini +++ /dev/null @@ -1,14 +0,0 @@ -[009.html] - type: testharness - [HTTP Referer should use the pushed state] - expected: FAIL - - [document.referrer should use the pushed state] - expected: FAIL - - [HTTP Referer should use the replaced state] - expected: FAIL - - [document.referrer should use the replaced state] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/010.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/010.html.ini deleted file mode 100644 index cd895f71c05b..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/010.html.ini +++ /dev/null @@ -1,14 +0,0 @@ -[010.html] - type: testharness - [HTTP Referer should use the pushed state (before onload)] - expected: FAIL - - [document.referrer should use the pushed state (before onload)] - expected: FAIL - - [HTTP Referer should use the replaced state (before onload)] - expected: FAIL - - [document.referrer should use the replaced state (before onload)] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/011.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/011.html.ini deleted file mode 100644 index 0867b0401e09..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/011.html.ini +++ /dev/null @@ -1,11 +0,0 @@ -[011.html] - type: testharness - [pushState should be able to set the location state] - expected: FAIL - - [pushed location should be reflected immediately] - expected: FAIL - - [pushed location should be retained after the page has loaded] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/012.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/012.html.ini index b153bc5f21ce..6d4c74df6ee9 100644 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/012.html.ini +++ b/tests/wpt/metadata/html/browsers/history/the-history-interface/012.html.ini @@ -1,8 +1,5 @@ [012.html] type: testharness - [replaceState should be able to set the location state] - expected: FAIL - [replaced location should be reflected immediately] expected: FAIL diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_001.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_001.html.ini deleted file mode 100644 index 6c1948b3884f..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_001.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[combination_history_001.html] - type: testharness - [Combine pushState and replaceSate methods] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_002.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_002.html.ini deleted file mode 100644 index b298a5d7c07c..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_002.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[combination_history_002.html] - type: testharness - [After calling of pushState method, check length] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_003.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_003.html.ini deleted file mode 100644 index 0329eef96dec..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_003.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[combination_history_003.html] - type: testharness - [After calling of pushState and replaceState methods, check length] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_004.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_004.html.ini deleted file mode 100644 index 703bccbb3347..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_004.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[combination_history_004.html] - type: testharness - [After calling of back method, check length] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_005.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_005.html.ini deleted file mode 100644 index 969bd2d84f1b..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_005.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[combination_history_005.html] - type: testharness - [After calling of forward method, check length] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_006.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_006.html.ini deleted file mode 100644 index 416b9e58d13c..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/combination_history_006.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[combination_history_006.html] - type: testharness - [After calling of go method, check length] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_back.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_back.html.ini deleted file mode 100644 index 5e01759025de..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_back.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_back.html] - type: testharness - [history back] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_forward.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_forward.html.ini deleted file mode 100644 index d3c12080dfa9..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_forward.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_forward.html] - type: testharness - [history forward] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_go_minus.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_go_minus.html.ini deleted file mode 100644 index 01f21b309605..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_go_minus.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_go_minus.html] - type: testharness - [history go minus] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_go_plus.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_go_plus.html.ini deleted file mode 100644 index e2999746053f..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_go_plus.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_go_plus.html] - type: testharness - [history go plus] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_pushstate.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_pushstate.html.ini deleted file mode 100644 index d9c5e0b38af1..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_pushstate.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_pushstate.html] - type: testharness - [history pushState] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_pushstate_err.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_pushstate_err.html.ini deleted file mode 100644 index 5a4e399ac99d..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_pushstate_err.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_pushstate_err.html] - type: testharness - [history pushState SECURITY_ERR] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_pushstate_nooptionalparam.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_pushstate_nooptionalparam.html.ini deleted file mode 100644 index 40a1deb0cc3e..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_pushstate_nooptionalparam.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_pushstate_nooptionalparam.html] - type: testharness - [history pushState NoOptionalParam] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_replacestate.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_replacestate.html.ini deleted file mode 100644 index e2068698c0d6..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_replacestate.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_replacestate.html] - type: testharness - [history replaceState] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_replacestate_err.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_replacestate_err.html.ini deleted file mode 100644 index 316097c5b4d1..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_replacestate_err.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_replacestate_err.html] - type: testharness - [history replaceState SECURITY_ERR] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_replacestate_nooptionalparam.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_replacestate_nooptionalparam.html.ini deleted file mode 100644 index d523d0b78d0c..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_replacestate_nooptionalparam.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_replacestate_nooptionalparam.html] - type: testharness - [history replaceStateNoOptionalParam] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_state.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/history_state.html.ini deleted file mode 100644 index 2819759ee82b..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/history_state.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[history_state.html] - type: testharness - [history state] - expected: FAIL -