diff options
author | Gregory Terzian <gterzian@users.noreply.github.com> | 2018-03-18 13:20:24 +0800 |
---|---|---|
committer | Gregory Terzian <gterzian@users.noreply.github.com> | 2018-05-05 19:14:36 +0800 |
commit | 427eaed535dfdeaf735a40e3cb82ad7077aa86c5 (patch) | |
tree | cc723041b13093541e8d5986b74bbd02e1c920a6 /components/script/dom | |
parent | a1d1b187102b340ba65955f2c4f1131c8276d050 (diff) | |
download | servo-427eaed535dfdeaf735a40e3cb82ad7077aa86c5.tar.gz servo-427eaed535dfdeaf735a40e3cb82ad7077aa86c5.zip |
beforeunload and unload infrastructure
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/document.rs | 145 | ||||
-rw-r--r-- | components/script/dom/event.rs | 9 | ||||
-rw-r--r-- | components/script/dom/eventtarget.rs | 19 | ||||
-rw-r--r-- | components/script/dom/window.rs | 23 |
4 files changed, 177 insertions, 19 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 9a33121b5ae..4fa33056253 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -63,7 +63,8 @@ use dom::keyboardevent::KeyboardEvent; use dom::location::Location; use dom::messageevent::MessageEvent; use dom::mouseevent::MouseEvent; -use dom::node::{self, CloneChildrenFlag, Node, NodeDamage, window_from_node, NodeFlags, LayoutNodeHelpers}; +use dom::node::{self, CloneChildrenFlag, document_from_node, window_from_node}; +use dom::node::{Node, NodeDamage, NodeFlags, LayoutNodeHelpers}; use dom::node::VecPreOrderInsertionHelper; use dom::nodeiterator::NodeIterator; use dom::nodelist::NodeList; @@ -348,6 +349,8 @@ pub struct Document { last_click_info: DomRefCell<Option<(Instant, Point2D<f32>)>>, /// <https://html.spec.whatwg.org/multipage/#ignore-destructive-writes-counter> ignore_destructive_writes_counter: Cell<u32>, + /// <https://html.spec.whatwg.org/multipage/#ignore-opens-during-unload-counter> + ignore_opens_during_unload_counter: Cell<u32>, /// The number of spurious `requestAnimationFrame()` requests we've received. /// /// A rAF request is considered spurious if nothing was actually reflowed. @@ -375,6 +378,10 @@ pub struct Document { throw_on_dynamic_markup_insertion_counter: Cell<u64>, /// https://html.spec.whatwg.org/multipage/#page-showing page_showing: Cell<bool>, + /// Whether the document is salvageable. + salvageable: Cell<bool>, + /// Whether the unload event has already been fired. + fired_unload: Cell<bool>, } #[derive(JSTraceable, MallocSizeOf)] @@ -483,18 +490,21 @@ impl Document { self.dirty_all_nodes(); self.window().reflow(ReflowGoal::Full, ReflowReason::CachedPageNeededReflow); self.window().resume(); - // html.spec.whatwg.org/multipage/browsing-the-web.html#history-traversal - // Step 6 + // html.spec.whatwg.org/multipage/#history-traversal + // Step 4.6 if self.ready_state.get() == DocumentReadyState::Complete { let document = Trusted::new(self); self.window.dom_manipulation_task_source().queue( task!(fire_pageshow_event: move || { let document = document.root(); let window = document.window(); - if document.page_showing.get() || !window.is_alive() { + // Step 4.6.1 + if document.page_showing.get() { return; } + // Step 4.6.2 document.page_showing.set(true); + // Step 4.6.4 let event = PageTransitionEvent::new( window, atom!("pageshow"), @@ -1623,6 +1633,112 @@ impl Document { ScriptThread::mark_document_with_no_blocked_loads(self); } + // https://html.spec.whatwg.org/multipage/#prompt-to-unload-a-document + pub fn prompt_to_unload(&self, recursive_flag: bool) -> bool { + // TODO: Step 1, increase the event loop's termination nesting level by 1. + // Step 2 + self.incr_ignore_opens_during_unload_counter(); + //Step 3-5. + let document = Trusted::new(self); + let beforeunload_event = BeforeUnloadEvent::new(&self.window, + atom!("beforeunload"), + EventBubbles::Bubbles, + EventCancelable::Cancelable); + let event = beforeunload_event.upcast::<Event>(); + event.set_trusted(true); + self.window.upcast::<EventTarget>().dispatch_event_with_target( + document.root().upcast(), + &event, + ); + // TODO: Step 6, decrease the event loop's termination nesting level by 1. + // Step 7 + if event.get_cancel_state() != EventDefault::Allowed { + self.salvageable.set(false); + } + let mut can_unload = true; + // TODO: Step 8 send a message to embedder to prompt user. + // Step 9 + if !recursive_flag { + for iframe in self.iter_iframes() { + // TODO: handle the case of cross origin iframes. + let document = document_from_node(&*iframe); + if !document.prompt_to_unload(true) { + self.salvageable.set(document.salvageable()); + can_unload = false; + break; + } + } + } + // Step 10 + self.decr_ignore_opens_during_unload_counter(); + can_unload + } + + // https://html.spec.whatwg.org/multipage/#unload-a-document + pub fn unload(&self, recursive_flag: bool, recycle: bool) { + // TODO: Step 1, increase the event loop's termination nesting level by 1. + // Step 2 + self.incr_ignore_opens_during_unload_counter(); + let document = Trusted::new(self); + // Step 3-6 + if self.page_showing.get() { + self.page_showing.set(false); + let event = PageTransitionEvent::new( + &self.window, + atom!("pagehide"), + false, // bubbles + false, // cancelable + self.salvageable.get(), // persisted + ); + let event = event.upcast::<Event>(); + event.set_trusted(true); + let _ = self.window.upcast::<EventTarget>().dispatch_event_with_target( + document.root().upcast(), + &event, + ); + // TODO Step 6, document visibility steps. + } + let mut event_handled = false; + // Step 7 + if !self.fired_unload.get() { + let event = Event::new( + &self.window.upcast(), + atom!("unload"), + EventBubbles::Bubbles, + EventCancelable::Cancelable, + ); + event.set_trusted(true); + let _ = self.window.upcast::<EventTarget>().dispatch_event_with_target( + document.root().upcast(), + &event, + ); + self.fired_unload.set(true); + event_handled = event.get_cancel_state() != EventDefault::Allowed; + } + // TODO: Step 8, decrease the event loop's termination nesting level by 1. + // Step 9 + self.salvageable.set(!event_handled); + // Step 13 + if !recursive_flag { + for iframe in self.iter_iframes() { + // TODO: handle the case of cross origin iframes. + let document = document_from_node(&*iframe); + document.unload(true, recycle); + if !document.salvageable() { + self.salvageable.set(false); + } + } + } + // Step 10, 14 + if !self.salvageable.get() { + // https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps + let msg = ScriptMsg::DiscardDocument; + let _ = self.window.upcast::<GlobalScope>().script_to_constellation_chan().send(msg); + } + // Step 15, End + self.decr_ignore_opens_during_unload_counter(); + } + // https://html.spec.whatwg.org/multipage/#the-end pub fn maybe_queue_document_completion(&self) { if self.loader.borrow().is_blocked() { @@ -2297,6 +2413,7 @@ impl Document { target_element: MutNullableDom::new(None), last_click_info: DomRefCell::new(None), ignore_destructive_writes_counter: Default::default(), + ignore_opens_during_unload_counter: Default::default(), spurious_animation_frames: Cell::new(0), dom_count: Cell::new(1), fullscreen_element: MutNullableDom::new(None), @@ -2306,6 +2423,8 @@ impl Document { canceller: canceller, throw_on_dynamic_markup_insertion_counter: Cell::new(0), page_showing: Cell::new(false), + salvageable: Cell::new(true), + fired_unload: Cell::new(false) } } @@ -2485,6 +2604,10 @@ impl Document { self.stylesheets.borrow().len() } + pub fn salvageable(&self) -> bool { + self.salvageable.get() + } + pub fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> { let stylesheets = self.stylesheets.borrow(); @@ -2608,6 +2731,20 @@ impl Document { self.ignore_destructive_writes_counter.get() - 1); } + pub fn is_prompting_or_unloading(&self) -> bool { + self.ignore_opens_during_unload_counter.get() > 0 + } + + fn incr_ignore_opens_during_unload_counter(&self) { + self.ignore_opens_during_unload_counter.set( + self.ignore_opens_during_unload_counter.get() + 1); + } + + fn decr_ignore_opens_during_unload_counter(&self) { + self.ignore_opens_during_unload_counter.set( + self.ignore_opens_during_unload_counter.get() - 1); + } + /// Whether we've seen so many spurious animation frames (i.e. animation frames that didn't /// mutate the DOM) that we've decided to fall back to fake ones. fn is_faking_animation_frames(&self) -> bool { diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs index 607469ca6bd..aab86758e23 100644 --- a/components/script/dom/event.rs +++ b/components/script/dom/event.rs @@ -482,7 +482,14 @@ fn invoke(window: Option<&Window>, event.current_target.set(Some(object)); // Step 5. - inner_invoke(window, object, event, &listeners); + if inner_invoke(window, object, event, &listeners) { + // <https://html.spec.whatwg.org/multipage/#unload-a-document> + // Step 9. + // Required to establish the 'salvageable' state of document as part of unloading. + if event.canceled.get() != EventDefault::Prevented { + event.mark_as_handled(); + } + } // TODO: step 6. } diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs index f909d97e253..df15ff1b2bc 100644 --- a/components/script/dom/eventtarget.rs +++ b/components/script/dom/eventtarget.rs @@ -187,22 +187,21 @@ impl CompiledEventListener { CommonEventHandler::BeforeUnloadEventHandler(ref handler) => { if let Some(event) = event.downcast::<BeforeUnloadEvent>() { - let rv = event.ReturnValue(); - + // Step 5 if let Ok(value) = handler.Call_(object, event.upcast::<Event>(), exception_handle) { - match value { - Some(value) => { - if rv.is_empty() { - event.SetReturnValue(value); - } - } - None => { - event.upcast::<Event>().PreventDefault(); + let rv = event.ReturnValue(); + if let Some(v) = value { + if rv.is_empty() { + event.SetReturnValue(v); } + event.upcast::<Event>().PreventDefault(); } } + } else { + // Step 5, "Otherwise" clause + let _ = handler.Call_(object, event.upcast::<Event>(), exception_handle); } } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 3a672291e8d..88156737e11 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1550,7 +1550,7 @@ impl Window { // https://html.spec.whatwg.org/multipage/#navigating-across-documents if !force_reload && url.as_url()[..Position::AfterQuery] == doc.url().as_url()[..Position::AfterQuery] { - // Step 5 + // Step 6 if let Some(fragment) = url.fragment() { doc.check_and_scroll_fragment(fragment); doc.set_url(url.clone()); @@ -1559,9 +1559,24 @@ impl Window { } let pipeline_id = self.upcast::<GlobalScope>().pipeline_id(); - self.main_thread_script_chan().send( - MainThreadScriptMsg::Navigate(pipeline_id, - LoadData::new(url, Some(pipeline_id), referrer_policy, Some(doc.url())), replace)).unwrap(); + + // Step 4 + let window_proxy = self.window_proxy(); + if let Some(active) = window_proxy.currently_active() { + if pipeline_id == active { + if doc.is_prompting_or_unloading() { + return; + } + } + } + + // Step 7 + if doc.prompt_to_unload(false) { + self.main_thread_script_chan().send( + MainThreadScriptMsg::Navigate(pipeline_id, + LoadData::new(url, Some(pipeline_id), referrer_policy, Some(doc.url())), replace)).unwrap(); + }; + } pub fn handle_fire_timer(&self, timer_id: TimerEventId) { |