aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/atoms/static_atoms.txt1
-rw-r--r--components/constellation/constellation.rs144
-rw-r--r--components/constellation/pipeline.rs14
-rw-r--r--components/constellation/session_history.rs40
-rw-r--r--components/msg/constellation_msg.rs36
-rw-r--r--components/net/http_loader.rs6
-rw-r--r--components/net/resource_thread.rs9
-rw-r--r--components/net_traits/lib.rs5
-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
-rw-r--r--components/script_traits/lib.rs7
-rw-r--r--components/script_traits/script_msg.rs6
14 files changed, 315 insertions, 56 deletions
diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt
index e72e1e07b19..3554a2e7a3b 100644
--- a/components/atoms/static_atoms.txt
+++ b/components/atoms/static_atoms.txt
@@ -52,6 +52,7 @@ password
pause
play
playing
+popstate
print
progress
radio
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index 016762b4b16..620e4b61460 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -111,7 +111,7 @@ use ipc_channel::ipc::{self, IpcSender, IpcReceiver};
use ipc_channel::router::ROUTER;
use layout_traits::LayoutThreadFactory;
use log::{Log, Level, LevelFilter, Metadata, Record};
-use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, PipelineId};
+use msg::constellation_msg::{BrowsingContextId, PipelineId, HistoryStateId, TopLevelBrowsingContextId};
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection};
use net_traits::{self, IpcSend, FetchResponseMsg, ResourceThreads};
@@ -1073,6 +1073,15 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
debug!("constellation got traverse history message from script");
self.handle_traverse_history_msg(source_top_ctx_id, direction);
}
+ // Handle a push history state request.
+ FromScriptMsg::PushHistoryState(history_state_id) => {
+ debug!("constellation got push history state message from script");
+ self.handle_push_history_state_msg(source_pipeline_id, history_state_id);
+ }
+ FromScriptMsg::ReplaceHistoryState(history_state_id) => {
+ debug!("constellation got replace history state message from script");
+ self.handle_replace_history_state_msg(source_pipeline_id, history_state_id);
+ }
// Handle a joint session history length request.
FromScriptMsg::JointSessionHistoryLength(sender) => {
debug!("constellation got joint session history length message from script");
@@ -1929,6 +1938,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
direction: TraversalDirection)
{
let mut browsing_context_changes = HashMap::<BrowsingContextId, NeedsToReload>::new();
+ let mut pipeline_changes = HashMap::<PipelineId, Option<HistoryStateId>>::new();
{
let session_history = self.joint_session_histories
.entry(top_level_browsing_context_id).or_insert(JointSessionHistory::new());
@@ -1945,7 +1955,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
match diff {
SessionHistoryDiff::BrowsingContextDiff { browsing_context_id, ref new_reloader, .. } => {
browsing_context_changes.insert(browsing_context_id, new_reloader.clone());
- }
+ },
+ SessionHistoryDiff::PipelineDiff { ref pipeline_reloader, new_history_state_id, .. } => {
+ // TODO(cbrewster): Handle the case where the pipeline needs to be reloaded.
+ // We should use the history state URL to change the URL that is reloaded.
+ if let NeedsToReload::No(pipeline_id) = *pipeline_reloader {
+ pipeline_changes.insert(pipeline_id, Some(new_history_state_id));
+ }
+ },
}
session_history.past.push(diff);
}
@@ -1961,7 +1978,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
match diff {
SessionHistoryDiff::BrowsingContextDiff { browsing_context_id, ref old_reloader, .. } => {
browsing_context_changes.insert(browsing_context_id, old_reloader.clone());
- }
+ },
+ SessionHistoryDiff::PipelineDiff { ref pipeline_reloader, old_history_state_id, .. } => {
+ // TODO(cbrewster): Handle the case where the pipeline needs to be reloaded.
+ // We should use the history state URL to change the URL that is reloaded.
+ if let NeedsToReload::No(pipeline_id) = *pipeline_reloader {
+ pipeline_changes.insert(pipeline_id, old_history_state_id);
+ }
+ },
}
session_history.future.push(diff);
}
@@ -1973,6 +1997,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.update_browsing_context(browsing_context_id, pipeline_id);
}
+ for (pipeline_id, history_state_id) in pipeline_changes.drain() {
+ self.update_pipeline(pipeline_id, history_state_id);
+ }
+
self.notify_history_changed(top_level_browsing_context_id);
self.trim_history(top_level_browsing_context_id);
@@ -2048,6 +2076,20 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
}
+ fn update_pipeline(&mut self, pipeline_id: PipelineId, history_state_id: Option<HistoryStateId>) {
+ let result = match self.pipelines.get_mut(&pipeline_id) {
+ None => return warn!("Pipeline {} history state updated after closure", pipeline_id),
+ Some(pipeline) => {
+ let msg = ConstellationControlMsg::UpdateHistoryStateId(pipeline_id, history_state_id);
+ pipeline.history_state_id = history_state_id;
+ pipeline.event_loop.send(msg)
+ },
+ };
+ if let Err(e) = result {
+ self.handle_send_error(pipeline_id, e);
+ }
+ }
+
fn handle_joint_session_history_length(&self,
top_level_browsing_context_id: TopLevelBrowsingContextId,
sender: IpcSender<u32>)
@@ -2058,6 +2100,35 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let _ = sender.send(length as u32);
}
+ fn handle_push_history_state_msg(&mut self, pipeline_id: PipelineId, history_state_id: HistoryStateId) {
+ let (top_level_browsing_context_id, old_state_id) = match self.pipelines.get_mut(&pipeline_id) {
+ Some(pipeline) => {
+ let old_history_state_id = pipeline.history_state_id;
+ pipeline.history_state_id = Some(history_state_id);
+ pipeline.history_states.insert(history_state_id);
+ (pipeline.top_level_browsing_context_id, old_history_state_id)
+ }
+ None => return warn!("Push history state {} for closed pipeline {}", history_state_id, pipeline_id),
+ };
+
+ let session_history = self.get_joint_session_history(top_level_browsing_context_id);
+ let diff = SessionHistoryDiff::PipelineDiff {
+ pipeline_reloader: NeedsToReload::No(pipeline_id),
+ new_history_state_id: history_state_id,
+ old_history_state_id: old_state_id,
+ };
+ session_history.push_diff(diff);
+ }
+
+ fn handle_replace_history_state_msg(&mut self, pipeline_id: PipelineId, history_state_id: HistoryStateId) {
+ match self.pipelines.get_mut(&pipeline_id) {
+ Some(pipeline) => {
+ pipeline.history_state_id = Some(history_state_id);
+ }
+ None => return warn!("Replace history state {} for closed pipeline {}", history_state_id, pipeline_id),
+ }
+ }
+
fn handle_key_msg(&mut self, ch: Option<char>, key: Key, state: KeyState, mods: KeyModifiers) {
// Send to the explicitly focused pipeline. If it doesn't exist, fall back to sending to
// the compositor.
@@ -2311,38 +2382,44 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// If LoadData was ignored, use the LoadData of the previous SessionHistoryEntry, which
// is the LoadData of the parent browsing context.
let resolve_load_data_future = |previous_load_data: &mut LoadData, diff: &SessionHistoryDiff| {
- let SessionHistoryDiff::BrowsingContextDiff { browsing_context_id, ref new_reloader, .. } = *diff;
-
- if browsing_context_id == top_level_browsing_context_id {
- let load_data = match *new_reloader {
- NeedsToReload::No(pipeline_id) => match self.pipelines.get(&pipeline_id) {
- Some(pipeline) => pipeline.load_data.clone(),
- None => previous_load_data.clone(),
- },
- NeedsToReload::Yes(_, ref load_data) => load_data.clone(),
- };
- *previous_load_data = load_data.clone();
- Some(load_data)
- } else {
- Some(previous_load_data.clone())
+ match *diff {
+ SessionHistoryDiff::BrowsingContextDiff { browsing_context_id, ref new_reloader, .. } => {
+ if browsing_context_id == top_level_browsing_context_id {
+ let load_data = match *new_reloader {
+ NeedsToReload::No(pipeline_id) => match self.pipelines.get(&pipeline_id) {
+ Some(pipeline) => pipeline.load_data.clone(),
+ None => previous_load_data.clone(),
+ },
+ NeedsToReload::Yes(_, ref load_data) => load_data.clone(),
+ };
+ *previous_load_data = load_data.clone();
+ Some(load_data)
+ } else {
+ Some(previous_load_data.clone())
+ }
+ },
+ SessionHistoryDiff::PipelineDiff { .. } => Some(previous_load_data.clone()),
}
};
let resolve_load_data_past = |previous_load_data: &mut LoadData, diff: &SessionHistoryDiff| {
- let SessionHistoryDiff::BrowsingContextDiff { browsing_context_id, ref old_reloader, .. } = *diff;
-
- if browsing_context_id == top_level_browsing_context_id {
- let load_data = match *old_reloader {
- NeedsToReload::No(pipeline_id) => match self.pipelines.get(&pipeline_id) {
- Some(pipeline) => pipeline.load_data.clone(),
- None => previous_load_data.clone(),
- },
- NeedsToReload::Yes(_, ref load_data) => load_data.clone(),
- };
- *previous_load_data = load_data.clone();
- Some(load_data)
- } else {
- Some(previous_load_data.clone())
+ match *diff {
+ SessionHistoryDiff::BrowsingContextDiff { browsing_context_id, ref old_reloader, .. } => {
+ if browsing_context_id == top_level_browsing_context_id {
+ let load_data = match *old_reloader {
+ NeedsToReload::No(pipeline_id) => match self.pipelines.get(&pipeline_id) {
+ Some(pipeline) => pipeline.load_data.clone(),
+ None => previous_load_data.clone(),
+ },
+ NeedsToReload::Yes(_, ref load_data) => load_data.clone(),
+ };
+ *previous_load_data = load_data.clone();
+ Some(load_data)
+ } else {
+ Some(previous_load_data.clone())
+ }
+ },
+ SessionHistoryDiff::PipelineDiff { .. } => Some(previous_load_data.clone()),
}
};
@@ -2433,7 +2510,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
};
session_history.push_diff(diff).into_iter()
- .map(|SessionHistoryDiff::BrowsingContextDiff { new_reloader, .. }| new_reloader)
+ .filter_map(|diff| match diff {
+ SessionHistoryDiff::BrowsingContextDiff { new_reloader, .. } => Some(new_reloader),
+ SessionHistoryDiff::PipelineDiff { .. } => None,
+ })
.filter_map(|pipeline_id| pipeline_id.alive_pipeline_id())
.collect::<Vec<_>>()
};
diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs
index 0e54e6649ba..548ef249cdc 100644
--- a/components/constellation/pipeline.rs
+++ b/components/constellation/pipeline.rs
@@ -16,7 +16,8 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use layout_traits::LayoutThreadFactory;
use metrics::PaintTimeMetrics;
-use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, PipelineId, PipelineNamespaceId};
+use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespaceId};
+use msg::constellation_msg::TopLevelBrowsingContextId;
use net::image_cache::ImageCacheImpl;
use net_traits::{IpcSend, ResourceThreads};
use net_traits::image_cache::ImageCache;
@@ -30,7 +31,7 @@ use script_traits::{ScriptThreadFactory, TimerSchedulerMsg, WindowSizeData};
use servo_config::opts::{self, Opts};
use servo_config::prefs::{PREFS, Pref};
use servo_url::ServoUrl;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
#[cfg(not(windows))]
use std::env;
use std::ffi::OsStr;
@@ -92,6 +93,12 @@ pub struct Pipeline {
/// The Load Data used to create this pipeline.
pub load_data: LoadData,
+
+ /// The active history state for this pipeline.
+ pub history_state_id: Option<HistoryStateId>,
+
+ /// The history states owned by this pipeline.
+ pub history_states: HashSet<HistoryStateId>,
}
/// Initial setup data needed to construct a pipeline.
@@ -157,7 +164,6 @@ pub struct InitialPipelineState {
/// Information about the page to load.
pub load_data: LoadData,
-
/// The ID of the pipeline namespace for this script thread.
pub pipeline_namespace_id: PipelineNamespaceId,
@@ -333,6 +339,8 @@ impl Pipeline {
visible: visible,
is_private: is_private,
load_data: load_data,
+ history_state_id: None,
+ history_states: HashSet::new(),
};
pipeline.notify_visibility();
diff --git a/components/constellation/session_history.rs b/components/constellation/session_history.rs
index e297acd1c21..44112ffe6c6 100644
--- a/components/constellation/session_history.rs
+++ b/components/constellation/session_history.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::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId};
+use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId};
use script_traits::LoadData;
use std::{fmt, mem};
use std::cmp::PartialEq;
@@ -44,11 +44,21 @@ impl JointSessionHistory {
}
pub fn remove_entries_for_browsing_context(&mut self, context_id: BrowsingContextId) {
- self.past.retain(|&SessionHistoryDiff::BrowsingContextDiff { browsing_context_id, .. }| {
- browsing_context_id != context_id
+ self.past.retain(|diff| {
+ match diff {
+ SessionHistoryDiff::BrowsingContextDiff { browsing_context_id, .. } => {
+ *browsing_context_id != context_id
+ },
+ SessionHistoryDiff::PipelineDiff { .. } => true,
+ }
});
- self.future.retain(|&SessionHistoryDiff::BrowsingContextDiff { browsing_context_id, .. }| {
- browsing_context_id != context_id
+ self.future.retain(|diff| {
+ match diff {
+ SessionHistoryDiff::BrowsingContextDiff { browsing_context_id, .. } => {
+ *browsing_context_id != context_id
+ },
+ SessionHistoryDiff::PipelineDiff { .. } => true,
+ }
});
}
}
@@ -130,6 +140,15 @@ pub enum SessionHistoryDiff {
/// The next pipeline (used when traversing into the future)
new_reloader: NeedsToReload,
},
+ /// Represents a diff where the active state of a pipeline changed.
+ PipelineDiff {
+ /// The pipeline whose history state changed.
+ pipeline_reloader: NeedsToReload,
+ /// The old history state id.
+ old_history_state_id: Option<HistoryStateId>,
+ /// The new history state id.
+ new_history_state_id: HistoryStateId,
+ },
}
impl SessionHistoryDiff {
@@ -141,7 +160,8 @@ impl SessionHistoryDiff {
NeedsToReload::No(pipeline_id) => Some(pipeline_id),
NeedsToReload::Yes(..) => None,
}
- }
+ },
+ SessionHistoryDiff::PipelineDiff { .. } => None,
}
}
@@ -153,7 +173,8 @@ impl SessionHistoryDiff {
NeedsToReload::No(pipeline_id) => Some(pipeline_id),
NeedsToReload::Yes(..) => None,
}
- }
+ },
+ SessionHistoryDiff::PipelineDiff { .. } => None,
}
}
@@ -168,6 +189,11 @@ impl SessionHistoryDiff {
*new_reloader = reloader.clone();
}
}
+ SessionHistoryDiff::PipelineDiff { ref mut pipeline_reloader, .. } => {
+ if *pipeline_reloader == *replaced_reloader {
+ *pipeline_reloader = reloader.clone();
+ }
+ }
}
}
}
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index 0bc88435072..f7dab637298 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -213,6 +213,13 @@ impl PipelineNamespace {
index: BrowsingContextIndex(self.next_index()),
}
}
+
+ fn next_history_state_id(&mut self) -> HistoryStateId {
+ HistoryStateId {
+ namespace_id: self.id,
+ index: HistoryStateIndex(self.next_index()),
+ }
+ }
}
thread_local!(pub static PIPELINE_NAMESPACE: Cell<Option<PipelineNamespace>> = Cell::new(None));
@@ -351,6 +358,35 @@ impl PartialEq<BrowsingContextId> for TopLevelBrowsingContextId {
}
}
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
+pub struct HistoryStateIndex(pub NonZeroU32);
+malloc_size_of_is_0!(HistoryStateIndex);
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize)]
+pub struct HistoryStateId {
+ pub namespace_id: PipelineNamespaceId,
+ pub index: HistoryStateIndex,
+}
+
+impl HistoryStateId {
+ pub fn new() -> HistoryStateId {
+ PIPELINE_NAMESPACE.with(|tls| {
+ let mut namespace = tls.get().expect("No namespace set for this thread!");
+ let next_history_state_id = namespace.next_history_state_id();
+ tls.set(Some(namespace));
+ next_history_state_id
+ })
+ }
+}
+
+impl fmt::Display for HistoryStateId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let HistoryStateIndex(index) = self.index;
+ write!(fmt, "({},{})", namespace_id, index.get())
+ }
+}
+
// We provide ids just for unit testing.
pub const TEST_NAMESPACE: PipelineNamespaceId = PipelineNamespaceId(1234);
#[allow(unsafe_code)]
diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs
index f1d18bda464..a8efbc4d297 100644
--- a/components/net/http_loader.rs
+++ b/components/net/http_loader.rs
@@ -32,7 +32,7 @@ use hyper::status::StatusCode;
use hyper_openssl::OpensslClient;
use hyper_serde::Serde;
use log;
-use msg::constellation_msg::PipelineId;
+use msg::constellation_msg::{HistoryStateId, PipelineId};
use net_traits::{CookieSource, FetchMetadata, NetworkError, ReferrerPolicy};
use net_traits::request::{CacheMode, CredentialsMode, Destination, Origin};
use net_traits::request::{RedirectMode, Referrer, Request, RequestMode};
@@ -40,7 +40,7 @@ use net_traits::request::{ResponseTainting, ServiceWorkersMode};
use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType};
use resource_thread::AuthCache;
use servo_url::{ImmutableOrigin, ServoUrl};
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::io::{self, Read, Write};
use std::iter::FromIterator;
@@ -73,6 +73,7 @@ pub struct HttpState {
pub cookie_jar: RwLock<CookieStorage>,
pub http_cache: RwLock<HttpCache>,
pub auth_cache: RwLock<AuthCache>,
+ pub history_states: RwLock<HashMap<HistoryStateId, Vec<u8>>>,
pub ssl_client: OpensslClient,
pub connector: Pool<Connector>,
}
@@ -83,6 +84,7 @@ impl HttpState {
hsts_list: RwLock::new(HstsList::new()),
cookie_jar: RwLock::new(CookieStorage::new(150)),
auth_cache: RwLock::new(AuthCache::new()),
+ history_states: RwLock::new(HashMap::new()),
http_cache: RwLock::new(HttpCache::new()),
ssl_client: ssl_client.clone(),
connector: create_http_connector(ssl_client),
diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs
index 8060b811bed..f070f58235d 100644
--- a/components/net/resource_thread.rs
+++ b/components/net/resource_thread.rs
@@ -131,6 +131,7 @@ fn create_http_states(config_dir: Option<&Path>) -> (Arc<HttpState>, Arc<HttpSta
auth_cache: RwLock::new(auth_cache),
http_cache: RwLock::new(http_cache),
hsts_list: RwLock::new(hsts_list),
+ history_states: RwLock::new(HashMap::new()),
ssl_client: ssl_client.clone(),
connector: create_http_connector(ssl_client),
};
@@ -243,6 +244,14 @@ impl ResourceChannelManager {
let cookies = cookie_jar.cookies_data_for_url(&url, source).map(Serde).collect();
consumer.send(cookies).unwrap();
}
+ CoreResourceMsg::GetHistoryState(history_state_id, consumer) => {
+ let history_states = http_state.history_states.read().unwrap();
+ consumer.send(history_states.get(&history_state_id).cloned()).unwrap();
+ }
+ CoreResourceMsg::SetHistoryState(history_state_id, history_state) => {
+ let mut history_states = http_state.history_states.write().unwrap();
+ history_states.insert(history_state_id, history_state);
+ }
CoreResourceMsg::Synchronize(sender) => {
let _ = sender.send(());
}
diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs
index 4fb8d28115f..e831892e144 100644
--- a/components/net_traits/lib.rs
+++ b/components/net_traits/lib.rs
@@ -34,6 +34,7 @@ use hyper_serde::Serde;
use ipc_channel::Error as IpcError;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
+use msg::constellation_msg::HistoryStateId;
use request::{Request, RequestInit};
use response::{HttpsState, Response, ResponseInit};
use servo_url::ServoUrl;
@@ -363,6 +364,10 @@ pub enum CoreResourceMsg {
GetCookiesForUrl(ServoUrl, IpcSender<Option<String>>, CookieSource),
/// Get a cookie by name for a given originating URL
GetCookiesDataForUrl(ServoUrl, IpcSender<Vec<Serde<Cookie<'static>>>>, CookieSource),
+ /// Get a history state by a given history state id
+ GetHistoryState(HistoryStateId, IpcSender<Option<Vec<u8>>>),
+ /// Set a history state for a given history state id
+ SetHistoryState(HistoryStateId, Vec<u8>),
/// Synchronization message solely for knowing the state of the ResourceChannelManager loop
Synchronize(IpcSender<()>),
/// Send the network sender in constellation to CoreResourceThread
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)
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index c65ad10be4b..d270d87a50c 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -48,8 +48,8 @@ use hyper::method::Method;
use ipc_channel::{Error as IpcError};
use ipc_channel::ipc::{IpcReceiver, IpcSender};
use libc::c_void;
-use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, Key, KeyModifiers, KeyState};
-use msg::constellation_msg::{PipelineId, PipelineNamespaceId, TraversalDirection};
+use msg::constellation_msg::{BrowsingContextId, HistoryStateId, Key, KeyModifiers, KeyState, PipelineId};
+use msg::constellation_msg::{PipelineNamespaceId, TraversalDirection, TopLevelBrowsingContextId};
use net_traits::{FetchResponseMsg, ReferrerPolicy, ResourceThreads};
use net_traits::image::base::Image;
use net_traits::image::base::PixelFormat;
@@ -289,6 +289,8 @@ pub enum ConstellationControlMsg {
/// Updates the current pipeline ID of a given iframe.
/// First PipelineId is for the parent, second is the new PipelineId for the frame.
UpdatePipelineId(PipelineId, BrowsingContextId, PipelineId, UpdatePipelineIdReason),
+ /// Updates the history state of a given pipeline.
+ UpdateHistoryStateId(PipelineId, Option<HistoryStateId>),
/// Set an iframe to be focused. Used when an element in an iframe gains focus.
/// PipelineId is for the parent, BrowsingContextId is for the nested browsing context
FocusIFrame(PipelineId, BrowsingContextId),
@@ -343,6 +345,7 @@ impl fmt::Debug for ConstellationControlMsg {
Navigate(..) => "Navigate",
PostMessage(..) => "PostMessage",
UpdatePipelineId(..) => "UpdatePipelineId",
+ UpdateHistoryStateId(..) => "UpdateHistoryStateId",
FocusIFrame(..) => "FocusIFrame",
WebDriverScriptCommand(..) => "WebDriverScriptCommand",
TickAllAnimations(..) => "TickAllAnimations",
diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs
index 9c53052fa58..43812ad097a 100644
--- a/components/script_traits/script_msg.rs
+++ b/components/script_traits/script_msg.rs
@@ -16,7 +16,7 @@ use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use euclid::{Size2D, TypedSize2D};
use gfx_traits::Epoch;
use ipc_channel::ipc::{IpcReceiver, IpcSender};
-use msg::constellation_msg::{BrowsingContextId, PipelineId, TraversalDirection};
+use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId, TraversalDirection};
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
use net_traits::CoreResourceMsg;
use net_traits::request::RequestInit;
@@ -104,6 +104,10 @@ pub enum ScriptMsg {
PostMessage(BrowsingContextId, Option<ImmutableOrigin>, Vec<u8>),
/// HTMLIFrameElement Forward or Back traversal.
TraverseHistory(TraversalDirection),
+ /// Inform the constellation of a pushed history state.
+ PushHistoryState(HistoryStateId),
+ /// Inform the constellation of a replaced history state.
+ ReplaceHistoryState(HistoryStateId),
/// Gets the length of the joint session history from the constellation.
JointSessionHistoryLength(IpcSender<u32>),
/// Favicon detected