1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 //! Tracking of pending loads in a document. 6 //! 7 //! <https://html.spec.whatwg.org/multipage/#the-end> 8 9 use dom::bindings::root::Dom; 10 use dom::document::Document; 11 use ipc_channel::ipc::IpcSender; 12 use net_traits::{CoreResourceMsg, FetchChannels, FetchResponseMsg}; 13 use net_traits::{ResourceThreads, IpcSend}; 14 use net_traits::request::RequestInit; 15 use servo_url::ServoUrl; 16 use std::thread; 17 18 #[derive(Clone, Debug, JSTraceable, MallocSizeOf, PartialEq)] 19 pub enum LoadType { 20 Image(ServoUrl), 21 Script(ServoUrl), 22 Subframe(ServoUrl), 23 Stylesheet(ServoUrl), 24 PageSource(ServoUrl), 25 Media, 26 } 27 28 impl LoadType { url(&self) -> Option<&ServoUrl>29 fn url(&self) -> Option<&ServoUrl> { 30 match *self { 31 LoadType::Image(ref url) | 32 LoadType::Script(ref url) | 33 LoadType::Subframe(ref url) | 34 LoadType::Stylesheet(ref url) | 35 LoadType::PageSource(ref url) => Some(url), 36 LoadType::Media => None, 37 } 38 } 39 } 40 41 /// Canary value ensuring that manually added blocking loads (ie. ones that weren't 42 /// created via DocumentLoader::fetch_async) are always removed by the time 43 /// that the owner is destroyed. 44 #[derive(JSTraceable, MallocSizeOf)] 45 #[must_root] 46 pub struct LoadBlocker { 47 /// The document whose load event is blocked by this object existing. 48 doc: Dom<Document>, 49 /// The load that is blocking the document's load event. 50 load: Option<LoadType>, 51 } 52 53 impl LoadBlocker { 54 /// Mark the document's load event as blocked on this new load. new(doc: &Document, load: LoadType) -> LoadBlocker55 pub fn new(doc: &Document, load: LoadType) -> LoadBlocker { 56 doc.loader_mut().add_blocking_load(load.clone()); 57 LoadBlocker { 58 doc: Dom::from_ref(doc), 59 load: Some(load), 60 } 61 } 62 63 /// Remove this load from the associated document's list of blocking loads. terminate(blocker: &mut Option<LoadBlocker>)64 pub fn terminate(blocker: &mut Option<LoadBlocker>) { 65 if let Some(this) = blocker.as_mut() { 66 this.doc.finish_load(this.load.take().unwrap()); 67 } 68 *blocker = None; 69 } 70 71 /// Return the url associated with this load. url(&self) -> Option<&ServoUrl>72 pub fn url(&self) -> Option<&ServoUrl> { 73 self.load.as_ref().and_then(LoadType::url) 74 } 75 } 76 77 impl Drop for LoadBlocker { drop(&mut self)78 fn drop(&mut self) { 79 if !thread::panicking() { 80 debug_assert!(self.load.is_none()); 81 } 82 } 83 } 84 85 #[derive(JSTraceable, MallocSizeOf)] 86 pub struct DocumentLoader { 87 resource_threads: ResourceThreads, 88 blocking_loads: Vec<LoadType>, 89 events_inhibited: bool, 90 } 91 92 impl DocumentLoader { new(existing: &DocumentLoader) -> DocumentLoader93 pub fn new(existing: &DocumentLoader) -> DocumentLoader { 94 DocumentLoader::new_with_threads(existing.resource_threads.clone(), None) 95 } 96 new_with_threads(resource_threads: ResourceThreads, initial_load: Option<ServoUrl>) -> DocumentLoader97 pub fn new_with_threads(resource_threads: ResourceThreads, 98 initial_load: Option<ServoUrl>) -> DocumentLoader { 99 debug!("Initial blocking load {:?}.", initial_load); 100 let initial_loads = initial_load.into_iter().map(LoadType::PageSource).collect(); 101 102 DocumentLoader { 103 resource_threads: resource_threads, 104 blocking_loads: initial_loads, 105 events_inhibited: false, 106 } 107 } 108 109 /// Add a load to the list of blocking loads. add_blocking_load(&mut self, load: LoadType)110 fn add_blocking_load(&mut self, load: LoadType) { 111 debug!("Adding blocking load {:?} ({}).", load, self.blocking_loads.len()); 112 self.blocking_loads.push(load); 113 } 114 115 /// Initiate a new fetch. fetch_async(&mut self, load: LoadType, request: RequestInit, fetch_target: IpcSender<FetchResponseMsg>)116 pub fn fetch_async(&mut self, 117 load: LoadType, 118 request: RequestInit, 119 fetch_target: IpcSender<FetchResponseMsg>) { 120 self.add_blocking_load(load); 121 self.fetch_async_background(request, fetch_target); 122 } 123 124 /// Initiate a new fetch that does not block the document load event. fetch_async_background(&self, request: RequestInit, fetch_target: IpcSender<FetchResponseMsg>)125 pub fn fetch_async_background(&self, 126 request: RequestInit, 127 fetch_target: IpcSender<FetchResponseMsg>) { 128 self.resource_threads.sender().send( 129 CoreResourceMsg::Fetch(request, FetchChannels::ResponseMsg(fetch_target, None))).unwrap(); 130 } 131 132 /// Mark an in-progress network request complete. finish_load(&mut self, load: &LoadType)133 pub fn finish_load(&mut self, load: &LoadType) { 134 debug!("Removing blocking load {:?} ({}).", load, self.blocking_loads.len()); 135 let idx = self.blocking_loads.iter().position(|unfinished| *unfinished == *load); 136 self.blocking_loads.remove(idx.unwrap_or_else(|| panic!("unknown completed load {:?}", load))); 137 } 138 is_blocked(&self) -> bool139 pub fn is_blocked(&self) -> bool { 140 // TODO: Ensure that we report blocked if parsing is still ongoing. 141 !self.blocking_loads.is_empty() 142 } 143 is_only_blocked_by_iframes(&self) -> bool144 pub fn is_only_blocked_by_iframes(&self) -> bool { 145 self.blocking_loads.iter().all(|load| match *load { 146 LoadType::Subframe(_) => true, 147 _ => false 148 }) 149 } 150 inhibit_events(&mut self)151 pub fn inhibit_events(&mut self) { 152 self.events_inhibited = true; 153 } 154 events_inhibited(&self) -> bool155 pub fn events_inhibited(&self) -> bool { 156 self.events_inhibited 157 } 158 resource_threads(&self) -> &ResourceThreads159 pub fn resource_threads(&self) -> &ResourceThreads { 160 &self.resource_threads 161 } 162 } 163