aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/bindings/trace.rs4
-rw-r--r--components/script/dom/history.rs78
-rw-r--r--components/script/dom/popstateevent.rs8
-rw-r--r--components/script/script_thread.rs13
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)