diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/bindings/trace.rs | 4 | ||||
-rw-r--r-- | components/script/dom/history.rs | 78 | ||||
-rw-r--r-- | components/script/dom/popstateevent.rs | 8 | ||||
-rw-r--r-- | components/script/script_thread.rs | 13 |
4 files changed, 94 insertions, 9 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 44369fef756..a12d9f5140a 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -65,7 +65,7 @@ use js::rust::{GCMethods, Handle, Runtime}; use js::typedarray::TypedArray; use js::typedarray::TypedArrayElement; use metrics::{InteractiveMetrics, InteractiveWindow}; -use msg::constellation_msg::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId}; +use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId}; use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads}; use net_traits::filemanager_thread::RelativePos; use net_traits::image::base::{Image, ImageMetadata}; @@ -356,7 +356,7 @@ 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!(DocumentActivity, WindowSizeData, WindowSizeType); -unsafe_no_jsmanaged_fields!(BrowsingContextId, PipelineId, TopLevelBrowsingContextId); +unsafe_no_jsmanaged_fields!(BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId); unsafe_no_jsmanaged_fields!(TimerEventId, TimerSource); unsafe_no_jsmanaged_fields!(TimelineMarkerType); unsafe_no_jsmanaged_fields!(WorkerId); diff --git a/components/script/dom/history.rs b/components/script/dom/history.rs index 4b296fb236f..b158a9d44fd 100644 --- a/components/script/dom/history.rs +++ b/components/script/dom/history.rs @@ -12,15 +12,20 @@ use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; use dom::bindings::root::{Dom, DomRoot}; use dom::bindings::str::{DOMString, USVString}; use dom::bindings::structuredclone::StructuredCloneData; +use dom::eventtarget::EventTarget; use dom::globalscope::GlobalScope; +use dom::popstateevent::PopStateEvent; use dom::window::Window; use dom_struct::dom_struct; use js::jsapi::{Heap, JSContext}; use js::jsval::{JSVal, NullValue, UndefinedValue}; use js::rust::HandleValue; -use msg::constellation_msg::TraversalDirection; +use msg::constellation_msg::{HistoryStateId, TraversalDirection}; +use net_traits::{CoreResourceMsg, IpcSend}; +use profile_traits::ipc; use profile_traits::ipc::channel; use script_traits::ScriptMsg; +use std::cell::Cell; enum PushOrReplace { Push, @@ -33,6 +38,7 @@ pub struct History { reflector_: Reflector, window: Dom<Window>, state: Heap<JSVal>, + state_id: Cell<Option<HistoryStateId>>, } impl History { @@ -43,6 +49,7 @@ impl History { reflector_: Reflector::new(), window: Dom::from_ref(&window), state: state, + state_id: Cell::new(None), } } @@ -63,6 +70,38 @@ impl History { Ok(()) } + #[allow(unsafe_code)] + pub fn activate_state(&self, state_id: Option<HistoryStateId>) { + self.state_id.set(state_id); + let serialized_data = match state_id { + Some(state_id) => { + let (tx, rx) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); + let _ = self.window + .upcast::<GlobalScope>() + .resource_threads() + .send(CoreResourceMsg::GetHistoryState(state_id, tx)); + rx.recv().unwrap() + }, + None => None, + }; + + match serialized_data { + Some(serialized_data) => { + let global_scope = self.window.upcast::<GlobalScope>(); + rooted!(in(global_scope.get_cx()) let mut state = UndefinedValue()); + StructuredCloneData::Vector(serialized_data).read(&global_scope, state.handle_mut()); + self.state.set(state.get()); + }, + None => { + self.state.set(NullValue()); + } + } + + unsafe { + PopStateEvent::dispatch_jsval(self.window.upcast::<EventTarget>(), &*self.window, self.state.handle()); + } + } + // https://html.spec.whatwg.org/multipage/#dom-history-pushstate // https://html.spec.whatwg.org/multipage/#dom-history-replacestate fn push_or_replace_state(&self, @@ -70,7 +109,7 @@ impl History { data: HandleValue, _title: DOMString, _url: Option<USVString>, - _push_or_replace: PushOrReplace) -> ErrorResult { + push_or_replace: PushOrReplace) -> ErrorResult { // Step 1 let document = self.window.Document(); @@ -85,13 +124,40 @@ impl History { // TODO: Step 4 // Step 5 - let serialized_data = StructuredCloneData::write(cx, data)?; + let serialized_data = StructuredCloneData::write(cx, data)?.move_to_arraybuffer(); // TODO: Steps 6-7 Url Handling // https://github.com/servo/servo/issues/19157 - // TODO: Step 8 Push/Replace session history entry - // https://github.com/servo/servo/issues/19156 + // Step 8 + let state_id = match push_or_replace { + PushOrReplace::Push => { + let state_id = HistoryStateId::new(); + self.state_id.set(Some(state_id)); + let msg = ScriptMsg::PushHistoryState(state_id); + let _ = self.window.upcast::<GlobalScope>().script_to_constellation_chan().send(msg); + state_id + }, + PushOrReplace::Replace => { + let state_id = match self.state_id.get() { + Some(state_id) => state_id, + None => { + let state_id = HistoryStateId::new(); + self.state_id.set(Some(state_id)); + state_id + }, + }; + let msg = ScriptMsg::ReplaceHistoryState(state_id); + let _ = self.window.upcast::<GlobalScope>().script_to_constellation_chan().send(msg); + state_id + }, + }; + + let _ = self.window + .upcast::<GlobalScope>() + .resource_threads() + .send(CoreResourceMsg::SetHistoryState(state_id, serialized_data.clone())); + // TODO: Step 9 Update current entry to represent a GET request // https://github.com/servo/servo/issues/19156 @@ -102,7 +168,7 @@ impl History { // Step 11 let global_scope = self.window.upcast::<GlobalScope>(); rooted!(in(cx) let mut state = UndefinedValue()); - serialized_data.read(&global_scope, state.handle_mut()); + StructuredCloneData::Vector(serialized_data).read(&global_scope, state.handle_mut()); // Step 12 self.state.set(state.get()); diff --git a/components/script/dom/popstateevent.rs b/components/script/dom/popstateevent.rs index e483a9a55a5..a10d19e3002 100644 --- a/components/script/dom/popstateevent.rs +++ b/components/script/dom/popstateevent.rs @@ -12,6 +12,7 @@ use dom::bindings::root::DomRoot; use dom::bindings::str::DOMString; use dom::bindings::trace::RootedTraceableBox; use dom::event::Event; +use dom::eventtarget::EventTarget; use dom::window::Window; use dom_struct::dom_struct; use js::jsapi::{Heap, JSContext}; @@ -66,6 +67,13 @@ impl PopStateEvent { init.parent.cancelable, init.state.handle())) } + + pub fn dispatch_jsval(target: &EventTarget, + window: &Window, + state: HandleValue) { + let event = PopStateEvent::new(window, atom!("popstate"), true, false, state); + event.upcast::<Event>().fire(target); + } } impl PopStateEventMethods for PopStateEvent { diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 6244228dc99..c188caed353 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -76,7 +76,8 @@ use js::jsapi::{JSTracer, SetWindowProxyClass}; use js::jsval::UndefinedValue; use metrics::{MAX_TASK_NS, PaintTimeMetrics}; use microtask::{MicrotaskQueue, Microtask}; -use msg::constellation_msg::{BrowsingContextId, PipelineId, PipelineNamespace, TopLevelBrowsingContextId}; +use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId}; +use msg::constellation_msg::{PipelineNamespace, TopLevelBrowsingContextId}; use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg}; use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads}; use net_traits::image_cache::{ImageCache, PendingImageResponse}; @@ -1168,6 +1169,7 @@ impl ScriptThread { Navigate(id, ..) => Some(id), PostMessage(id, ..) => Some(id), UpdatePipelineId(_, _, id, _) => Some(id), + UpdateHistoryStateId(id, ..) => Some(id), FocusIFrame(id, ..) => Some(id), WebDriverScriptCommand(id, ..) => Some(id), TickAllAnimations(id) => Some(id), @@ -1294,6 +1296,8 @@ impl ScriptThread { browsing_context_id, new_pipeline_id, reason), + ConstellationControlMsg::UpdateHistoryStateId(pipeline_id, history_state_id) => + self.handle_update_history_state_id_msg(pipeline_id, history_state_id), ConstellationControlMsg::FocusIFrame(parent_pipeline_id, frame_id) => self.handle_focus_iframe_msg(parent_pipeline_id, frame_id), ConstellationControlMsg::WebDriverScriptCommand(pipeline_id, msg) => @@ -1672,6 +1676,13 @@ impl ScriptThread { } } + fn handle_update_history_state_id_msg(&self, pipeline_id: PipelineId, history_state_id: Option<HistoryStateId>) { + match { self.documents.borrow().find_window(pipeline_id) } { + None => return warn!("update history state after pipeline {} closed.", pipeline_id), + Some(window) => window.History().r().activate_state(history_state_id), + } + } + /// Window was resized, but this script was not active, so don't reflow yet fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: WindowSizeData) { let window = self.documents.borrow().find_window(id) |