From 1e6293ea1d06120c9f3488d7d32c24d8d92df6b1 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Thu, 9 Jun 2016 18:52:52 +0530 Subject: [PATCH] Integrate service worker manager thread --- components/constellation/constellation.rs | 105 +++++++- components/constellation/pipeline.rs | 10 +- components/gfx/font_cache_thread.rs | 5 +- components/net/file_loader.rs | 5 +- components/net/http_loader.rs | 34 +-- components/net/image_cache_thread.rs | 5 +- components/net/resource_thread.rs | 10 +- components/net_traits/lib.rs | 31 +-- components/script/document_loader.rs | 7 +- components/script/dom/abstractworker.rs | 26 -- components/script/dom/bindings/global.rs | 10 +- .../script/dom/dedicatedworkerglobalscope.rs | 37 ++- components/script/dom/serviceworker.rs | 81 ++---- .../script/dom/serviceworkercontainer.rs | 2 +- .../script/dom/serviceworkerglobalscope.rs | 230 ++++++------------ .../script/dom/serviceworkerregistration.rs | 44 +++- components/script/dom/window.rs | 10 +- components/script/dom/worker.rs | 26 +- components/script/dom/workerglobalscope.rs | 93 ++----- components/script/dom/xmlhttprequest.rs | 9 +- components/script/lib.rs | 9 +- components/script/script_thread.rs | 77 +++--- components/script/serviceworker_manager.rs | 123 ++++++++++ components/script_traits/lib.rs | 54 +++- components/script_traits/script_msg.rs | 45 ++++ components/servo/lib.rs | 31 ++- tests/html/service-worker/assets/tty.gif | Bin 0 -> 62998 bytes tests/html/service-worker/dashboard-page.html | 14 ++ tests/html/service-worker/demo_iframe.html | 13 + tests/html/service-worker/dummy.js | 1 + tests/html/service-worker/iframe_sw.js | 1 + tests/html/service-worker/index.html | 43 ++++ tests/html/service-worker/profile.html | 14 ++ tests/html/service-worker/sw.js | 1 + tests/html/service-worker/sw_dashboard.js | 2 + tests/html/service-worker/sw_profile.js | 1 + tests/unit/net/data_loader.rs | 5 +- tests/unit/net/http_loader.rs | 127 ++++------ tests/unit/net/resource_thread.rs | 5 +- 39 files changed, 764 insertions(+), 582 deletions(-) create mode 100644 components/script/serviceworker_manager.rs create mode 100644 tests/html/service-worker/assets/tty.gif create mode 100644 tests/html/service-worker/dashboard-page.html create mode 100644 tests/html/service-worker/demo_iframe.html create mode 100644 tests/html/service-worker/dummy.js create mode 100644 tests/html/service-worker/iframe_sw.js create mode 100644 tests/html/service-worker/index.html create mode 100644 tests/html/service-worker/profile.html create mode 100644 tests/html/service-worker/sw.js create mode 100644 tests/html/service-worker/sw_dashboard.js create mode 100644 tests/html/service-worker/sw_profile.js diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index b262684d5d9..81587cf0c1b 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -35,19 +35,20 @@ use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::filemanager_thread::FileManagerThreadMsg; use net_traits::image_cache_thread::ImageCacheThread; use net_traits::storage_thread::StorageThreadMsg; -use net_traits::{self, ResourceThreads, IpcSend}; +use net_traits::{self, ResourceThreads, IpcSend, CustomResponseMediator, CoreResourceMsg}; use offscreen_gl_context::{GLContextAttributes, GLLimits}; use pipeline::{ChildProcess, InitialPipelineState, Pipeline}; use profile_traits::mem; use profile_traits::time; use rand::{random, Rng, SeedableRng, StdRng}; -use script_traits::webdriver_msg; use script_traits::{AnimationState, AnimationTickType, CompositorEvent}; use script_traits::{ConstellationControlMsg, ConstellationMsg as FromCompositorMsg}; use script_traits::{DocumentState, LayoutControlMsg}; use script_traits::{IFrameLoadInfo, IFrameSandboxState, TimerEventRequest}; use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory}; -use script_traits::{LogEntry, MozBrowserEvent, MozBrowserErrorType, WebDriverCommandMsg, WindowSizeData}; +use script_traits::{MozBrowserEvent, MozBrowserErrorType, WebDriverCommandMsg, WindowSizeData}; +use script_traits::{ScopeThings, SWManagerMsg}; +use script_traits::{webdriver_msg, LogEntry, ServiceWorkerMsg}; use std::borrow::ToOwned; use std::collections::{HashMap, VecDeque}; use std::io::Error as IOError; @@ -97,6 +98,9 @@ pub struct Constellation { /// Receives messages from scripts. script_receiver: Receiver, + /// Receive messages from resource thread + resource_receiver: Receiver, + /// Receives messages from the compositor compositor_receiver: Receiver, @@ -125,6 +129,15 @@ pub struct Constellation { /// A channel through which messages can be sent to the bluetooth thread. bluetooth_thread: IpcSender, + /// Sender to Service Worker Manager thread + swmanager_chan: Option>, + + /// to send messages to this object + swmanager_sender: IpcSender, + + /// to receive sw manager message + swmanager_receiver: Receiver, + /// A list of all the pipelines. (See the `pipeline` module for more details.) pipelines: HashMap, @@ -410,9 +423,13 @@ impl Constellation where LTF: LayoutThreadFactory, STF: ScriptThreadFactory { - pub fn start(state: InitialConstellationState) -> Sender { + pub fn start(state: InitialConstellationState) -> (Sender, IpcSender) { let (compositor_sender, compositor_receiver) = channel(); + // service worker manager to communicate with constellation + let (swmanager_sender, swmanager_receiver) = ipc::channel().expect("ipc channel failure"); + let sw_mgr_clone = swmanager_sender.clone(); + spawn_named("Constellation".to_owned(), move || { let (ipc_script_sender, ipc_script_receiver) = ipc::channel().expect("ipc channel failure"); let script_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_script_receiver); @@ -423,6 +440,15 @@ impl Constellation let (ipc_panic_sender, ipc_panic_receiver) = ipc::channel().expect("ipc channel failure"); let panic_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_panic_receiver); + let (resource_ipc_sender, resource_ipc_receiver) = ipc::channel().expect("ipc channel failure"); + let resource_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(resource_ipc_receiver); + + state.public_resource_threads.sender() + .send(CoreResourceMsg::NetworkMediator(resource_ipc_sender)) + .expect("network sender sending failure"); + + let swmanager_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(swmanager_receiver); + let mut constellation: Constellation = Constellation { script_sender: ipc_script_sender, layout_sender: ipc_layout_sender, @@ -438,6 +464,10 @@ impl Constellation private_resource_threads: state.private_resource_threads, image_cache_thread: state.image_cache_thread, font_cache_thread: state.font_cache_thread, + swmanager_chan: None, + swmanager_receiver: swmanager_receiver, + swmanager_sender: sw_mgr_clone, + resource_receiver: resource_receiver, pipelines: HashMap::new(), frames: HashMap::new(), subpage_map: HashMap::new(), @@ -482,7 +512,7 @@ impl Constellation PipelineNamespace::install(namespace_id); constellation.run(); }); - compositor_sender + (compositor_sender, swmanager_sender) } fn run(&mut self) { @@ -534,6 +564,7 @@ impl Constellation compositor_proxy: self.compositor_proxy.clone_compositor_proxy(), devtools_chan: self.devtools_chan.clone(), bluetooth_thread: self.bluetooth_thread.clone(), + swmanager_thread: self.swmanager_sender.clone(), image_cache_thread: self.image_cache_thread.clone(), font_cache_thread: self.font_cache_thread.clone(), resource_threads: resource_threads, @@ -607,6 +638,8 @@ impl Constellation Compositor(FromCompositorMsg), Layout(FromLayoutMsg), Panic(PanicMsg), + FromSWManager(SWManagerMsg), + FromResource(CustomResponseMediator), } // Get one incoming request. @@ -625,6 +658,8 @@ impl Constellation let receiver_from_compositor = &self.compositor_receiver; let receiver_from_layout = &self.layout_receiver; let receiver_from_panic = &self.panic_receiver; + let receiver_from_swmanager = &self.swmanager_receiver; + let receiver_from_resource = &self.resource_receiver; select! { msg = receiver_from_script.recv() => Request::Script(msg.expect("Unexpected script channel panic in constellation")), @@ -633,7 +668,11 @@ impl Constellation msg = receiver_from_layout.recv() => Request::Layout(msg.expect("Unexpected layout channel panic in constellation")), msg = receiver_from_panic.recv() => - Request::Panic(msg.expect("Unexpected panic channel panic in constellation")) + Request::Panic(msg.expect("Unexpected panic channel panic in constellation")), + msg = receiver_from_swmanager.recv() => + Request::FromSWManager(msg.expect("Unexpected panic channel panic in constellation")), + msg = receiver_from_resource.recv() => + Request::FromResource(msg.expect("Unexpected panic channel panic in constellation")) } }; @@ -650,6 +689,29 @@ impl Constellation Request::Panic(message) => { self.handle_request_from_panic(message); }, + Request::FromSWManager(message) => { + self.handle_request_from_swmanager(message); + } + Request::FromResource(message) => { + self.handle_request_from_resource(message); + } + } + } + + fn handle_request_from_swmanager(&mut self, message: SWManagerMsg) { + match message { + SWManagerMsg::OwnSender(sw_sender) => { + // store service worker manager for communicating with it. + self.swmanager_chan = Some(sw_sender); + } + } + } + + fn handle_request_from_resource(&self, mediator: CustomResponseMediator) { + if let Some(ref mgr) = self.swmanager_chan { + let _ = mgr.send(ServiceWorkerMsg::ActivateWorker(mediator)); + } else { + warn!("activation request to service worker manager failed"); } } @@ -923,6 +985,14 @@ impl Constellation FromScriptMsg::GetScrollOffset(pid, lid, send) => { self.compositor_proxy.send(ToCompositorMsg::GetScrollOffset(pid, lid, send)); } + FromScriptMsg::NetworkRequest(mediator) => { + debug!("activation request for service worker received"); + self.handle_activate_worker(mediator); + } + FromScriptMsg::RegisterServiceWorker(scope_things, scope) => { + debug!("constellation got store registration scope message"); + self.handle_register_serviceworker(scope_things, scope); + } } } @@ -950,6 +1020,22 @@ impl Constellation } } + fn handle_register_serviceworker(&self, scope_things: ScopeThings, scope: Url) { + if let Some(ref mgr) = self.swmanager_chan { + let _ = mgr.send(ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope)); + } else { + warn!("sending scope info to service worker manager failed"); + } + } + + fn handle_activate_worker(&self, mediator: CustomResponseMediator) { + if let Some(ref mgr) = self.swmanager_chan { + let _ = mgr.send(ServiceWorkerMsg::ActivateWorker(mediator)); + } else { + warn!("activation request to service worker manager failed"); + } + } + fn handle_exit(&mut self) { // TODO: add a timer, which forces shutdown if threads aren't responsive. if self.shutting_down { return; } @@ -999,6 +1085,13 @@ impl Constellation warn!("Exit bluetooth thread failed ({})", e); } + debug!("Exiting service worker manager thread."); + if let Some(mgr) = self.swmanager_chan.as_ref() { + if let Err(e) = mgr.send(ServiceWorkerMsg::Exit) { + warn!("Exit service worker manager failed ({})", e); + } + } + debug!("Exiting font cache thread."); self.font_cache_thread.exit(); diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index c07891984ed..240ee99e4f7 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -25,7 +25,7 @@ use net_traits::image_cache_thread::ImageCacheThread; use profile_traits::mem as profile_mem; use profile_traits::time; use script_traits::{ConstellationControlMsg, InitialScriptState, MozBrowserEvent}; -use script_traits::{LayoutControlMsg, LayoutMsg, NewLayoutInfo, ScriptMsg}; +use script_traits::{LayoutControlMsg, LayoutMsg, NewLayoutInfo, ScriptMsg, SWManagerMsg}; use script_traits::{ScriptThreadFactory, TimerEventRequest, WindowSizeData}; use std::collections::HashMap; use std::io::Error as IOError; @@ -99,6 +99,8 @@ pub struct InitialPipelineState { pub devtools_chan: Option>, /// A channel to the bluetooth thread. pub bluetooth_thread: IpcSender, + /// A channel to the service worker manager thread + pub swmanager_thread: IpcSender, /// A channel to the image cache thread. pub image_cache_thread: ImageCacheThread, /// A channel to the font cache thread. @@ -222,6 +224,7 @@ impl Pipeline { scheduler_chan: state.scheduler_chan, devtools_chan: script_to_devtools_chan, bluetooth_thread: state.bluetooth_thread, + swmanager_thread: state.swmanager_thread, image_cache_thread: state.image_cache_thread, font_cache_thread: state.font_cache_thread, resource_threads: state.resource_threads, @@ -414,6 +417,7 @@ pub struct UnprivilegedPipelineContent { scheduler_chan: IpcSender, devtools_chan: Option>, bluetooth_thread: IpcSender, + swmanager_thread: IpcSender, image_cache_thread: ImageCacheThread, font_cache_thread: FontCacheThread, resource_threads: ResourceThreads, @@ -546,4 +550,8 @@ impl UnprivilegedPipelineContent { pub fn prefs(&self) -> HashMap { self.prefs.clone() } + + pub fn swmanager_chan(&self) -> IpcSender { + self.swmanager_thread.clone() + } } diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index 6e60e2b6dd2..314def3b2cf 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -6,7 +6,7 @@ use font_template::{FontTemplate, FontTemplateDescriptor}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use mime::{TopLevel, SubLevel}; -use net_traits::{AsyncResponseTarget, LoadContext, PendingAsyncLoad, CoreResourceThread, ResponseAction, RequestSource}; +use net_traits::{AsyncResponseTarget, LoadContext, PendingAsyncLoad, CoreResourceThread, ResponseAction}; use platform::font_context::FontContextHandle; use platform::font_list::SANS_SERIF_FONT_FAMILY; use platform::font_list::for_each_available_family; @@ -211,8 +211,7 @@ impl FontCache { url.clone(), None, None, - None, - RequestSource::None); + None); let (data_sender, data_receiver) = ipc::channel().unwrap(); let data_target = AsyncResponseTarget { sender: data_sender, diff --git a/components/net/file_loader.rs b/components/net/file_loader.rs index 271baca74f6..2edc9dfa15d 100644 --- a/components/net/file_loader.rs +++ b/components/net/file_loader.rs @@ -7,7 +7,7 @@ use mime_classifier::MimeClassifier; use mime_guess::guess_mime_type; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; use net_traits::ProgressMsg::{Done, Payload}; -use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError, LoadOrigin, RequestSource}; +use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError, LoadOrigin}; use resource_thread::{CancellationListener, ProgressSender}; use resource_thread::{send_error, start_sending_sniffed_opt}; use std::borrow::ToOwned; @@ -39,9 +39,6 @@ impl LoadOrigin for FileLoadOrigin { fn referrer_policy(&self) -> Option { None } - fn request_source(&self) -> RequestSource { - RequestSource::None - } fn pipeline_id(&self) -> Option { None } diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index 9a028e5ce59..d8344e12502 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -24,7 +24,7 @@ use hyper::method::Method; use hyper::mime::{Mime, SubLevel, TopLevel}; use hyper::net::Fresh; use hyper::status::{StatusClass, StatusCode}; -use ipc_channel::ipc; +use ipc_channel::ipc::{self, IpcSender}; use log; use mime_classifier::MimeClassifier; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; @@ -32,7 +32,7 @@ use net_traits::ProgressMsg::{Done, Payload}; use net_traits::hosts::replace_hosts; use net_traits::response::HttpsState; use net_traits::{CookieSource, IncludeSubdomains, LoadConsumer, LoadContext, LoadData}; -use net_traits::{Metadata, NetworkError, RequestSource, CustomResponse}; +use net_traits::{Metadata, NetworkError, CustomResponse, CustomResponseMediator}; use openssl; use openssl::ssl::error::{SslError, OpensslError}; use profile_traits::time::{ProfilerCategory, profile, ProfilerChan, TimerMetadata}; @@ -59,6 +59,7 @@ pub fn factory(user_agent: String, http_state: HttpState, devtools_chan: Option>, profiler_chan: ProfilerChan, + constellation_chan: Option>, connector: Arc>) -> Box>, http_state: HttpState, devtools_chan: Option>, + constellation_chan: Option>, cancel_listener: CancellationListener, user_agent: String) { let factory = NetworkHttpRequestFactory { @@ -140,7 +143,7 @@ fn load_for_consumer(load_data: LoadData, let ui_provider = TFDProvider; match load(&load_data, &ui_provider, &http_state, devtools_chan, &factory, - user_agent, &cancel_listener) { + user_agent, &cancel_listener, constellation_chan) { Err(error) => { match error.error { LoadErrorType::ConnectionAborted { .. } => unreachable!(), @@ -860,7 +863,8 @@ pub fn load(load_data: &LoadData, devtools_chan: Option>, request_factory: &HttpRequestFactory, user_agent: String, - cancel_listener: &CancellationListener) + cancel_listener: &CancellationListener, + constellation_chan: Option>) -> Result where A: HttpRequest + 'static, B: UIProvider { let max_redirects = PREFS.get("network.http.redirection-limit").as_i64().unwrap() as u32; let mut iters = 0; @@ -878,17 +882,19 @@ pub fn load(load_data: &LoadData, } let (msg_sender, msg_receiver) = ipc::channel().unwrap(); - match load_data.source { - RequestSource::Window(ref sender) | RequestSource::Worker(ref sender) => { - sender.send(msg_sender.clone()).unwrap(); - let received_msg = msg_receiver.recv().unwrap(); - if let Some(custom_response) = received_msg { - let metadata = Metadata::default(doc_url.clone()); - let readable_response = to_readable_response(custom_response); - return StreamedResponse::from_http_response(box readable_response, metadata); - } + let response_mediator = CustomResponseMediator { + response_chan: msg_sender, + load_url: doc_url.clone() + }; + if let Some(sender) = constellation_chan { + let _ = sender.send(response_mediator); + if let Ok(Some(custom_response)) = msg_receiver.try_recv() { + let metadata = Metadata::default(doc_url.clone()); + let readable_response = to_readable_response(custom_response); + return StreamedResponse::from_http_response(box readable_response, metadata); } - RequestSource::None => {} + } else { + debug!("Did not receive a custom response"); } // If the URL is a view-source scheme then the scheme data contains the diff --git a/components/net/image_cache_thread.rs b/components/net/image_cache_thread.rs index fd6f62ebbea..49a5a6e69f7 100644 --- a/components/net/image_cache_thread.rs +++ b/components/net/image_cache_thread.rs @@ -11,7 +11,7 @@ use net_traits::image_cache_thread::ImageResponder; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheCommand, ImageCacheThread, ImageState}; use net_traits::image_cache_thread::{ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, UsePlaceholder}; use net_traits::{AsyncResponseTarget, CoreResourceMsg, LoadConsumer, LoadData, CoreResourceThread, LoadOrigin}; -use net_traits::{ResponseAction, LoadContext, NetworkError, RequestSource}; +use net_traits::{ResponseAction, LoadContext, NetworkError}; use std::borrow::ToOwned; use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -313,9 +313,6 @@ impl LoadOrigin for ImageCacheOrigin { fn referrer_policy(&self) -> Option { None } - fn request_source(&self) -> RequestSource { - RequestSource::None - } fn pipeline_id(&self) -> Option { None } diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index 2bf6f38e331..64f96876ea7 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -30,7 +30,7 @@ use net_traits::request::{Request, RequestInit}; use net_traits::storage_thread::StorageThreadMsg; use net_traits::{AsyncResponseTarget, Metadata, ProgressMsg, ResponseAction, CoreResourceThread}; use net_traits::{CoreResourceMsg, CookieSource, FetchResponseMsg, FetchTaskTarget, LoadConsumer}; -use net_traits::{LoadData, LoadResponse, NetworkError, ResourceId}; +use net_traits::{LoadData, LoadResponse, NetworkError, ResourceId, CustomResponseMediator}; use net_traits::{WebSocketCommunicate, WebSocketConnectData, ResourceThreads}; use profile_traits::time::ProfilerChan; use rustc_serialize::json; @@ -202,7 +202,7 @@ pub fn new_core_resource_thread(user_agent: String, } struct ResourceChannelManager { - resource_manager: CoreResourceManager, + resource_manager: CoreResourceManager } fn create_resource_groups() -> (ResourceGroup, ResourceGroup) { @@ -279,6 +279,9 @@ impl ResourceChannelManager { let mut cookie_jar = group.cookie_jar.write().unwrap(); consumer.send(cookie_jar.cookies_for_url(&url, source)).unwrap(); } + CoreResourceMsg::NetworkMediator(mediator_chan) => { + self.resource_manager.constellation_chan = Some(mediator_chan) + } CoreResourceMsg::GetCookiesDataForUrl(url, consumer, source) => { let mut cookie_jar = group.cookie_jar.write().unwrap(); let cookies = cookie_jar.cookies_data_for_url(&url, source).collect(); @@ -456,6 +459,7 @@ pub struct CoreResourceManager { user_agent: String, mime_classifier: Arc, devtools_chan: Option>, + constellation_chan: Option>, profiler_chan: ProfilerChan, filemanager_chan: IpcSender, cancel_load_map: HashMap>, @@ -471,6 +475,7 @@ impl CoreResourceManager { user_agent: user_agent, mime_classifier: Arc::new(MimeClassifier::new()), devtools_chan: devtools_channel, + constellation_chan: None, profiler_chan: profiler_chan, filemanager_chan: filemanager_chan, cancel_load_map: HashMap::new(), @@ -542,6 +547,7 @@ impl CoreResourceManager { http_state, self.devtools_chan.clone(), self.profiler_chan.clone(), + self.constellation_chan.clone(), resource_grp.connector.clone()) }, "data" => from_factory(data_loader::factory), diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index 875bbd106aa..8e7bb996697 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -97,13 +97,10 @@ impl CustomResponse { } } -pub type CustomResponseSender = IpcSender>; - -#[derive(Clone, Deserialize, Serialize, HeapSizeOf)] -pub enum RequestSource { - Window(#[ignore_heap_size_of = "Defined in ipc-channel"] IpcSender), - Worker(#[ignore_heap_size_of = "Defined in ipc-channel"] IpcSender), - None +#[derive(Clone, Deserialize, Serialize)] +pub struct CustomResponseMediator { + pub response_chan: IpcSender>, + pub load_url: Url } #[derive(Clone, Deserialize, Serialize, HeapSizeOf)] @@ -126,9 +123,7 @@ pub struct LoadData { pub context: LoadContext, /// The policy and referring URL for the originator of this request pub referrer_policy: Option, - pub referrer_url: Option, - pub source: RequestSource, - + pub referrer_url: Option } impl LoadData { @@ -147,7 +142,6 @@ impl LoadData { context: context, referrer_policy: load_origin.referrer_policy(), referrer_url: load_origin.referrer_url().clone(), - source: load_origin.request_source() } } } @@ -155,7 +149,6 @@ impl LoadData { pub trait LoadOrigin { fn referrer_url(&self) -> Option; fn referrer_policy(&self) -> Option; - fn request_source(&self) -> RequestSource; fn pipeline_id(&self) -> Option; } @@ -436,6 +429,8 @@ pub enum CoreResourceMsg { Cancel(ResourceId), /// Synchronization message solely for knowing the state of the ResourceChannelManager loop Synchronize(IpcSender<()>), + /// Send the network sender in constellation to CoreResourceThread + NetworkMediator(IpcSender), /// Break the load handler loop, send a reply when done cleaning up local resources // and exit Exit(IpcSender<()>), @@ -451,8 +446,7 @@ pub struct PendingAsyncLoad { guard: PendingLoadGuard, context: LoadContext, referrer_policy: Option, - referrer_url: Option, - source: RequestSource + referrer_url: Option } struct PendingLoadGuard { @@ -480,9 +474,6 @@ impl LoadOrigin for PendingAsyncLoad { fn referrer_policy(&self) -> Option { self.referrer_policy.clone() } - fn request_source(&self) -> RequestSource { - self.source.clone() - } fn pipeline_id(&self) -> Option { self.pipeline } @@ -494,8 +485,7 @@ impl PendingAsyncLoad { url: Url, pipeline: Option, referrer_policy: Option, - referrer_url: Option, - source: RequestSource) + referrer_url: Option) -> PendingAsyncLoad { PendingAsyncLoad { core_resource_thread: core_resource_thread, @@ -504,8 +494,7 @@ impl PendingAsyncLoad { guard: PendingLoadGuard { loaded: false, }, context: context, referrer_policy: referrer_policy, - referrer_url: referrer_url, - source: source + referrer_url: referrer_url } } diff --git a/components/script/document_loader.rs b/components/script/document_loader.rs index a68038572f2..04e266994c2 100644 --- a/components/script/document_loader.rs +++ b/components/script/document_loader.rs @@ -8,8 +8,7 @@ use dom::bindings::js::JS; use dom::document::Document; use msg::constellation_msg::PipelineId; -use net_traits::{PendingAsyncLoad, LoadContext}; -use net_traits::{RequestSource, AsyncResponseTarget}; +use net_traits::{PendingAsyncLoad, AsyncResponseTarget, LoadContext}; use net_traits::{ResourceThreads, IpcSend}; use std::thread; use url::Url; @@ -130,14 +129,12 @@ impl DocumentLoader { let context = load.to_load_context(); let url = load.url().clone(); self.add_blocking_load(load); - let client_chan = referrer.window().custom_message_chan(); PendingAsyncLoad::new(context, self.resource_threads.sender(), url, self.pipeline, referrer.get_referrer_policy(), - Some(referrer.url().clone()), - RequestSource::Window(client_chan)) + Some(referrer.url().clone())) } /// Create and initiate a new network request. diff --git a/components/script/dom/abstractworker.rs b/components/script/dom/abstractworker.rs index f9ff583a0dd..4f2f029800b 100644 --- a/components/script/dom/abstractworker.rs +++ b/components/script/dom/abstractworker.rs @@ -8,10 +8,7 @@ use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; use js::jsapi::{JSRuntime, JS_RequestInterruptCallback}; use js::rust::Runtime; -use msg::constellation_msg::{PipelineId, ReferrerPolicy}; -use net_traits::{LoadOrigin, RequestSource}; use script_runtime::CommonScriptMsg; -use url::Url; /// Messages used to control the worker event loops pub enum WorkerScriptMsg { @@ -21,29 +18,6 @@ pub enum WorkerScriptMsg { DOMMessage(StructuredCloneData), } -#[derive(Clone)] -pub struct WorkerScriptLoadOrigin { - pub referrer_url: Option, - pub referrer_policy: Option, - pub request_source: RequestSource, - pub pipeline_id: Option -} - -impl LoadOrigin for WorkerScriptLoadOrigin { - fn referrer_url(&self) -> Option { - self.referrer_url.clone() - } - fn referrer_policy(&self) -> Option { - self.referrer_policy.clone() - } - fn request_source(&self) -> RequestSource { - self.request_source.clone() - } - fn pipeline_id(&self) -> Option { - self.pipeline_id.clone() - } -} - pub struct SimpleWorkerErrorHandler { pub addr: Trusted, } diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index 22cb256bd04..5adcbb67818 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -20,7 +20,7 @@ use js::jsapi::{JSContext, JSObject, JS_GetClass, MutableHandleValue}; use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; use msg::constellation_msg::{PipelineId, PanicMsg}; use net_traits::filemanager_thread::FileManagerThreadMsg; -use net_traits::{ResourceThreads, CoreResourceThread, RequestSource, IpcSend}; +use net_traits::{ResourceThreads, CoreResourceThread, IpcSend}; use profile_traits::{mem, time}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort}; use script_thread::{MainThreadScriptChan, ScriptThread, RunnableWrapper}; @@ -66,14 +66,6 @@ impl<'a> GlobalRef<'a> { } } - /// gets the custom message channel associated with global object - pub fn request_source(&self) -> RequestSource { - match *self { - GlobalRef::Window(ref window) => RequestSource::Window(window.custom_message_chan()), - GlobalRef::Worker(ref worker) => RequestSource::Worker(worker.custom_message_chan()), - } - } - /// Get the `PipelineId` for this global scope. pub fn pipeline(&self) -> PipelineId { match *self { diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 87ac897a4e4..cf5537676e0 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -4,7 +4,7 @@ use devtools; use devtools_traits::DevtoolScriptControlMsg; -use dom::abstractworker::{WorkerScriptLoadOrigin, WorkerScriptMsg, SharedRt , SimpleWorkerErrorHandler}; +use dom::abstractworker::{WorkerScriptMsg, SharedRt , SimpleWorkerErrorHandler}; use dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThreadWorkerChan}; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding; @@ -21,7 +21,6 @@ use dom::bindings::structuredclone::StructuredCloneData; use dom::messageevent::MessageEvent; use dom::worker::{TrustedWorkerAddress, WorkerMessageHandler}; use dom::workerglobalscope::WorkerGlobalScope; -use dom::workerglobalscope::WorkerGlobalScopeInit; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use js::jsapi::{HandleValue, JS_SetInterruptCallback}; @@ -29,12 +28,13 @@ use js::jsapi::{JSAutoCompartment, JSContext}; use js::jsval::UndefinedValue; use js::rust::Runtime; use msg::constellation_msg::PipelineId; -use net_traits::{LoadContext, load_whole_resource, CustomResponse, IpcSend}; +use net_traits::{LoadContext, load_whole_resource, IpcSend}; use rand::random; use script_runtime::ScriptThreadEventCategory::WorkerEvent; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, get_reports, new_rt_and_cx}; -use script_traits::{TimerEvent, TimerSource}; +use script_traits::{TimerEvent, TimerSource, WorkerScriptLoadOrigin, WorkerGlobalScopeInit}; use std::mem::replace; +use std::sync::atomic::AtomicBool; use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; use std::sync::{Arc, Mutex}; use url::Url; @@ -70,8 +70,7 @@ impl<'a> Drop for AutoWorkerReset<'a> { enum MixedMessage { FromWorker((TrustedWorkerAddress, WorkerScriptMsg)), FromScheduler((TrustedWorkerAddress, TimerEvent)), - FromDevtools(DevtoolScriptControlMsg), - FromNetwork(IpcSender>), + FromDevtools(DevtoolScriptControlMsg) } // https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope @@ -102,14 +101,16 @@ impl DedicatedWorkerGlobalScope { own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, timer_event_chan: IpcSender, - timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>) + timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, + closing: Arc) -> DedicatedWorkerGlobalScope { DedicatedWorkerGlobalScope { workerglobalscope: WorkerGlobalScope::new_inherited(init, worker_url, runtime, from_devtools_receiver, - timer_event_chan), + timer_event_chan, + Some(closing)), id: id, receiver: receiver, own_sender: own_sender, @@ -128,7 +129,8 @@ impl DedicatedWorkerGlobalScope { own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, timer_event_chan: IpcSender, - timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>) + timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, + closing: Arc) -> Root { let cx = runtime.cx(); let scope = box DedicatedWorkerGlobalScope::new_inherited(init, @@ -140,7 +142,8 @@ impl DedicatedWorkerGlobalScope { own_sender, receiver, timer_event_chan, - timer_event_port); + timer_event_port, + closing); DedicatedWorkerGlobalScopeBinding::Wrap(cx, scope) } @@ -154,7 +157,8 @@ impl DedicatedWorkerGlobalScope { parent_sender: Box, own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, - worker_load_origin: WorkerScriptLoadOrigin) { + worker_load_origin: WorkerScriptLoadOrigin, + closing: Arc) { let serialized_worker_url = worker_url.to_string(); let name = format!("WebWorker for {}", serialized_worker_url); let panic_chan = init.panic_chan.clone(); @@ -193,7 +197,7 @@ impl DedicatedWorkerGlobalScope { let global = DedicatedWorkerGlobalScope::new( init, url, id, devtools_mpsc_port, runtime, parent_sender.clone(), own_sender, receiver, - timer_ipc_chan, timer_rx); + timer_ipc_chan, timer_rx, closing); // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter // registration (#6631), so we instead use a random number and cross our fingers. let scope = global.upcast::(); @@ -254,20 +258,17 @@ impl DedicatedWorkerGlobalScope { let worker_port = &self.receiver; let timer_event_port = &self.timer_event_port; let devtools_port = scope.from_devtools_receiver(); - let msg_port = scope.custom_message_port(); let sel = Select::new(); let mut worker_handle = sel.handle(worker_port); let mut timer_event_handle = sel.handle(timer_event_port); let mut devtools_handle = sel.handle(devtools_port); - let mut msg_port_handle = sel.handle(msg_port); unsafe { worker_handle.add(); timer_event_handle.add(); if scope.from_devtools_sender().is_some() { devtools_handle.add(); } - msg_port_handle.add(); } let ret = sel.wait(); if ret == worker_handle.id() { @@ -276,8 +277,6 @@ impl DedicatedWorkerGlobalScope { Ok(MixedMessage::FromScheduler(try!(timer_event_port.recv()))) } else if ret == devtools_handle.id() { Ok(MixedMessage::FromDevtools(try!(devtools_port.recv()))) - } else if ret == msg_port_handle.id() { - Ok(MixedMessage::FromNetwork(try!(msg_port.recv()))) } else { panic!("unexpected select result!") } @@ -339,10 +338,6 @@ impl DedicatedWorkerGlobalScope { MixedMessage::FromWorker((linked_worker, msg)) => { let _ar = AutoWorkerReset::new(self, linked_worker); self.handle_script_event(msg); - }, - MixedMessage::FromNetwork(network_sender) => { - // We send None as of now - let _ = network_sender.send(None); } } } diff --git a/components/script/dom/serviceworker.rs b/components/script/dom/serviceworker.rs index ad303d45915..19af9b09ba4 100644 --- a/components/script/dom/serviceworker.rs +++ b/components/script/dom/serviceworker.rs @@ -2,8 +2,8 @@ * 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 dom::abstractworker::WorkerScriptMsg; use dom::abstractworker::{SimpleWorkerErrorHandler, WorkerErrorHandler}; -use dom::abstractworker::{WorkerScriptMsg, WorkerScriptLoadOrigin, SharedRt}; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::ServiceWorkerBinding::{ServiceWorkerMethods, ServiceWorkerState, Wrap}; @@ -13,19 +13,13 @@ use dom::bindings::js::Root; use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::{DOMString, USVString}; -use dom::client::Client; use dom::errorevent::ErrorEvent; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; -use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; -use dom::workerglobalscope::prepare_workerscope_init; -use ipc_channel::ipc; use js::jsval::UndefinedValue; use script_thread::Runnable; use std::cell::Cell; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::mpsc::{Sender, channel}; -use std::sync::{Arc, Mutex}; +use std::sync::mpsc::Sender; use url::Url; pub type TrustedServiceWorkerAddress = Trusted; @@ -35,36 +29,27 @@ pub struct ServiceWorker { eventtarget: EventTarget, script_url: DOMRefCell, state: Cell, - closing: Arc, #[ignore_heap_size_of = "Defined in std"] - sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, - #[ignore_heap_size_of = "Defined in rust-mozjs"] - runtime: Arc>>, + sender: Option>, skip_waiting: Cell } impl ServiceWorker { - fn new_inherited(sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, - closing: Arc, - script_url: &str, + fn new_inherited(script_url: &str, skip_waiting: bool) -> ServiceWorker { ServiceWorker { eventtarget: EventTarget::new_inherited(), - closing: closing, - sender: sender, + sender: None, script_url: DOMRefCell::new(String::from(script_url)), state: Cell::new(ServiceWorkerState::Installing), - runtime: Arc::new(Mutex::new(None)), skip_waiting: Cell::new(skip_waiting) } } pub fn new(global: GlobalRef, - closing: Arc, - sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, script_url: &str, skip_waiting: bool) -> Root { - reflect_dom_object(box ServiceWorker::new_inherited(sender, closing, script_url, skip_waiting), global, Wrap) + reflect_dom_object(box ServiceWorker::new_inherited(script_url, skip_waiting), global, Wrap) } pub fn dispatch_simple_error(address: TrustedServiceWorkerAddress) { @@ -72,23 +57,19 @@ impl ServiceWorker { service_worker.upcast().fire_simple_event("error"); } - pub fn is_closing(&self) -> bool { - self.closing.load(Ordering::SeqCst) - } - pub fn set_transition_state(&self, state: ServiceWorkerState) { self.state.set(state); self.upcast::().fire_simple_event("statechange"); } + pub fn get_script_url(&self) -> Url { + Url::parse(&self.script_url.borrow().clone()).unwrap() + } + pub fn handle_error_message(address: TrustedServiceWorkerAddress, message: DOMString, filename: DOMString, lineno: u32, colno: u32) { let worker = address.root(); - if worker.is_closing() { - return; - } - let global = worker.r().global(); rooted!(in(global.r().get_cx()) let error = UndefinedValue()); let errorevent = ErrorEvent::new(global.r(), atom!("error"), @@ -97,42 +78,12 @@ impl ServiceWorker { errorevent.upcast::().fire(worker.upcast()); } - #[allow(unsafe_code)] - pub fn init_service_worker(global: GlobalRef, - script_url: Url, - skip_waiting: bool) -> Root { - let (sender, receiver) = channel(); - let closing = Arc::new(AtomicBool::new(false)); - let worker = ServiceWorker::new(global, - closing.clone(), - sender.clone(), - script_url.as_str(), - skip_waiting); - let worker_ref = Trusted::new(worker.r()); - - let worker_load_origin = WorkerScriptLoadOrigin { - referrer_url: None, - referrer_policy: None, - request_source: global.request_source(), - pipeline_id: Some(global.pipeline()) - }; - - let (devtools_sender, devtools_receiver) = ipc::channel().unwrap(); - let init = prepare_workerscope_init(global, - "Service Worker".to_owned(), - script_url.clone(), - devtools_sender.clone(), - closing); - - // represents a service worker client - let sw_client = Client::new(global.as_window()); - let trusted_client = Trusted::new(&*sw_client); - - ServiceWorkerGlobalScope::run_serviceworker_scope( - init, script_url, global.pipeline(), devtools_receiver, worker.runtime.clone(), worker_ref, - global.script_chan(), sender, receiver, trusted_client, worker_load_origin); - - worker + pub fn install_serviceworker(global: GlobalRef, + script_url: Url, + skip_waiting: bool) -> Root { + ServiceWorker::new(global, + script_url.as_str(), + skip_waiting) } } diff --git a/components/script/dom/serviceworkercontainer.rs b/components/script/dom/serviceworkercontainer.rs index 744daba7b91..17fea7100be 100644 --- a/components/script/dom/serviceworkercontainer.rs +++ b/components/script/dom/serviceworkercontainer.rs @@ -98,7 +98,7 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer { script_url, scope_str.clone(), self); - ScriptThread::set_registration(scope, &*worker_registration); + ScriptThread::set_registration(scope, &*worker_registration, self.global().r().pipeline()); Ok(worker_registration) } } diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index cbe195d1b8e..4841e0ff08a 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -4,8 +4,7 @@ use devtools; use devtools_traits::DevtoolScriptControlMsg; -use dom::abstractworker::{WorkerScriptLoadOrigin, WorkerScriptMsg, SharedRt, SimpleWorkerErrorHandler}; -use dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThreadWorkerChan}; +use dom::abstractworker::WorkerScriptMsg; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding; @@ -13,66 +12,34 @@ use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding::ServiceWo use dom::bindings::global::{GlobalRef, global_root_from_context}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{Root, RootCollection}; -use dom::bindings::refcounted::{Trusted, LiveDOMReferences}; +use dom::bindings::refcounted::LiveDOMReferences; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; -use dom::client::Client; use dom::messageevent::MessageEvent; use dom::serviceworker::TrustedServiceWorkerAddress; use dom::workerglobalscope::WorkerGlobalScope; -use dom::workerglobalscope::WorkerGlobalScopeInit; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::{self, IpcSender, IpcReceiver}; use ipc_channel::router::ROUTER; use js::jsapi::{JS_SetInterruptCallback, JSAutoCompartment, JSContext}; use js::jsval::UndefinedValue; use js::rust::Runtime; use msg::constellation_msg::PipelineId; -use net_traits::{LoadContext, load_whole_resource, CustomResponse, IpcSend}; -use rand::random; -use script_runtime::ScriptThreadEventCategory::ServiceWorkerEvent; -use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, get_reports, new_rt_and_cx}; -use script_traits::{TimerEvent, TimerSource}; -use std::mem::replace; +use net_traits::{LoadContext, load_whole_resource, IpcSend}; +use script_runtime::{CommonScriptMsg, StackRootTLS, get_reports, new_rt_and_cx}; +use script_traits::{TimerEvent, WorkerGlobalScopeInit, ScopeThings, ServiceWorkerMsg}; use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; -use std::sync::{Arc, Mutex}; -use std::time::Instant; +use std::thread; +use std::time::Duration; use url::Url; use util::prefs::PREFS; use util::thread::spawn_named; use util::thread_state; use util::thread_state::{IN_WORKER, SCRIPT}; -/// Set the `worker` field of a related ServiceWorkerGlobalScope object to a particular -/// value for the duration of this object's lifetime. This ensures that the related Worker -/// object only lives as long as necessary (ie. while events are being executed), while -/// providing a reference that can be cloned freely. -struct AutoWorkerReset<'a> { - workerscope: &'a ServiceWorkerGlobalScope, - old_worker: Option, -} - -impl<'a> AutoWorkerReset<'a> { - fn new(workerscope: &'a ServiceWorkerGlobalScope, - worker: TrustedServiceWorkerAddress) - -> AutoWorkerReset<'a> { - AutoWorkerReset { - workerscope: workerscope, - old_worker: replace(&mut *workerscope.worker.borrow_mut(), Some(worker)), - } - } -} - -impl<'a> Drop for AutoWorkerReset<'a> { - fn drop(&mut self) { - *self.workerscope.worker.borrow_mut() = self.old_worker.clone(); - } -} - -enum MixedMessage { +pub enum MixedMessage { FromServiceWorker((TrustedServiceWorkerAddress, WorkerScriptMsg)), - FromScheduler((TrustedServiceWorkerAddress, TimerEvent)), FromDevtools(DevtoolScriptControlMsg), - FromNetwork(IpcSender>), + FromTimeoutThread(()), } #[dom_struct] @@ -84,14 +51,13 @@ pub struct ServiceWorkerGlobalScope { #[ignore_heap_size_of = "Defined in std"] own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, #[ignore_heap_size_of = "Defined in std"] - timer_event_port: Receiver<(TrustedServiceWorkerAddress, TimerEvent)>, + timer_event_port: Receiver<()>, #[ignore_heap_size_of = "Trusted has unclear ownership like JS"] worker: DOMRefCell>, - #[ignore_heap_size_of = "Can't measure trait objects"] - /// Sender to the parent thread. - parent_sender: Box, #[ignore_heap_size_of = "Defined in std"] - service_worker_client: Trusted + swmanager_sender: IpcSender, + #[ignore_heap_size_of = "Defined in std"] + scope_url: Url } impl ServiceWorkerGlobalScope { @@ -100,26 +66,27 @@ impl ServiceWorkerGlobalScope { id: PipelineId, from_devtools_receiver: Receiver, runtime: Runtime, - parent_sender: Box, own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, receiver: Receiver<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, timer_event_chan: IpcSender, - timer_event_port: Receiver<(TrustedServiceWorkerAddress, TimerEvent)>, - client: Trusted) + timer_event_port: Receiver<()>, + swmanager_sender: IpcSender, + scope_url: Url) -> ServiceWorkerGlobalScope { ServiceWorkerGlobalScope { workerglobalscope: WorkerGlobalScope::new_inherited(init, worker_url, runtime, from_devtools_receiver, - timer_event_chan), + timer_event_chan, + None), id: id, receiver: receiver, - own_sender: own_sender, timer_event_port: timer_event_port, - parent_sender: parent_sender, + own_sender: own_sender, worker: DOMRefCell::new(None), - service_worker_client: client + swmanager_sender: swmanager_sender, + scope_url: scope_url } } @@ -128,12 +95,12 @@ impl ServiceWorkerGlobalScope { id: PipelineId, from_devtools_receiver: Receiver, runtime: Runtime, - parent_sender: Box, own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, receiver: Receiver<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, timer_event_chan: IpcSender, - timer_event_port: Receiver<(TrustedServiceWorkerAddress, TimerEvent)>, - client: Trusted) + timer_event_port: Receiver<()>, + swmanager_sender: IpcSender, + scope_url: Url) -> Root { let cx = runtime.cx(); let scope = box ServiceWorkerGlobalScope::new_inherited(init, @@ -141,42 +108,39 @@ impl ServiceWorkerGlobalScope { id, from_devtools_receiver, runtime, - parent_sender, own_sender, receiver, timer_event_chan, timer_event_port, - client); + swmanager_sender, + scope_url); ServiceWorkerGlobalScopeBinding::Wrap(cx, scope) } #[allow(unsafe_code)] - pub fn run_serviceworker_scope(init: WorkerGlobalScopeInit, - worker_url: Url, - id: PipelineId, - from_devtools_receiver: IpcReceiver, - main_thread_rt: Arc>>, - worker: TrustedServiceWorkerAddress, - parent_sender: Box, + pub fn run_serviceworker_scope(scope_things: ScopeThings, own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, receiver: Receiver<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, - client: Trusted, - worker_load_origin: WorkerScriptLoadOrigin) { - let serialized_worker_url = worker_url.to_string(); + devtools_receiver: IpcReceiver, + swmanager_sender: IpcSender, + scope_url: Url) { + let ScopeThings { script_url, + pipeline_id, + init, + worker_load_origin, + .. } = scope_things; + + let serialized_worker_url = script_url.to_string(); spawn_named(format!("ServiceWorker for {}", serialized_worker_url), move || { thread_state::initialize(SCRIPT | IN_WORKER); - let roots = RootCollection::new(); let _stack_roots_tls = StackRootTLS::new(&roots); - let (url, source) = match load_whole_resource(LoadContext::Script, &init.resource_threads.sender(), - worker_url, + script_url, &worker_load_origin) { Err(_) => { println!("error loading script {}", serialized_worker_url); - parent_sender.send(CommonScriptMsg::RunnableMsg(ServiceWorkerEvent, - box SimpleWorkerErrorHandler::new(worker))).unwrap(); return; } Ok((metadata, bytes)) => { @@ -185,25 +149,18 @@ impl ServiceWorkerGlobalScope { }; let runtime = unsafe { new_rt_and_cx() }; - *main_thread_rt.lock().unwrap() = Some(SharedRt::new(&runtime)); let (devtools_mpsc_chan, devtools_mpsc_port) = channel(); - ROUTER.route_ipc_receiver_to_mpsc_sender(from_devtools_receiver, devtools_mpsc_chan); + ROUTER.route_ipc_receiver_to_mpsc_sender(devtools_receiver, devtools_mpsc_chan); - let (timer_tx, timer_rx) = channel(); - let (timer_ipc_chan, timer_ipc_port) = ipc::channel().unwrap(); - let worker_for_route = worker.clone(); - ROUTER.add_route(timer_ipc_port.to_opaque(), box move |message| { - let event = message.to().unwrap(); - timer_tx.send((worker_for_route.clone(), event)).unwrap(); - }); + // TODO XXXcreativcoder use this timer_ipc_port, when we have a service worker instance here + let (timer_ipc_chan, _timer_ipc_port) = ipc::channel().unwrap(); + let (timer_chan, timer_port) = channel(); let global = ServiceWorkerGlobalScope::new( - init, url, id, devtools_mpsc_port, runtime, - parent_sender.clone(), own_sender, receiver, - timer_ipc_chan, timer_rx, client); - // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter - // registration (#6631), so we instead use a random number and cross our fingers. + init, url, pipeline_id, devtools_mpsc_port, runtime, + own_sender, receiver, + timer_ipc_chan, timer_port, swmanager_sender, scope_url); let scope = global.upcast::(); unsafe { @@ -211,35 +168,25 @@ impl ServiceWorkerGlobalScope { JS_SetInterruptCallback(scope.runtime(), Some(interrupt_callback)); } - if scope.is_closing() { - return; - } + scope.execute_script(DOMString::from(source)); - { - let _ar = AutoWorkerReset::new(global.r(), worker); - scope.execute_script(DOMString::from(source)); - } - - - let reporter_name = format!("service-worker-reporter-{}", random::()); - scope.mem_profiler_chan().run_with_memory_reporting(|| { - // Service workers are time limited - let sw_lifetime = Instant::now(); + // Service workers are time limited + spawn_named("SWTimeoutThread".to_owned(), move || { let sw_lifetime_timeout = PREFS.get("dom.serviceworker.timeout_seconds").as_u64().unwrap(); - while let Ok(event) = global.receive_event() { - if scope.is_closing() { - break; - } - global.handle_event(event); - if sw_lifetime.elapsed().as_secs() == sw_lifetime_timeout { - break; - } + thread::sleep(Duration::new(sw_lifetime_timeout, 0)); + let _ = timer_chan.send(()); + }); + + // TODO XXXcreativcoder bring back run_with_memory_reporting when things are more concrete here. + while let Ok(event) = global.receive_event() { + if !global.handle_event(event) { + break; } - }, reporter_name, parent_sender, CommonScriptMsg::CollectReports); + } }); } - fn handle_event(&self, event: MixedMessage) { + fn handle_event(&self, event: MixedMessage) -> bool { match event { MixedMessage::FromDevtools(msg) => { let global_ref = GlobalRef::Worker(self.upcast()); @@ -252,26 +199,15 @@ impl ServiceWorkerGlobalScope { devtools::handle_wants_live_notifications(&global_ref, bool_val), _ => debug!("got an unusable devtools control message inside the worker!"), } - }, - MixedMessage::FromScheduler((linked_worker, timer_event)) => { - match timer_event { - TimerEvent(TimerSource::FromWorker, id) => { - let _ar = AutoWorkerReset::new(self, linked_worker); - let scope = self.upcast::(); - scope.handle_fire_timer(id); - }, - TimerEvent(_, _) => { - panic!("The service worker received a TimerEvent from a window.") - } - } + true } - MixedMessage::FromServiceWorker((linked_worker, msg)) => { - let _ar = AutoWorkerReset::new(self, linked_worker); + MixedMessage::FromServiceWorker((_, msg)) => { self.handle_script_event(msg); - }, - MixedMessage::FromNetwork(network_sender) => { - // We send None as of now - let _ = network_sender.send(None); + true + } + MixedMessage::FromTimeoutThread(_) => { + let _ = self.swmanager_sender.send(ServiceWorkerMsg::Timeout(self.scope_url.clone())); + false } } } @@ -307,44 +243,33 @@ impl ServiceWorkerGlobalScope { fn receive_event(&self) -> Result { let scope = self.upcast::(); let worker_port = &self.receiver; - let timer_event_port = &self.timer_event_port; let devtools_port = scope.from_devtools_receiver(); - let msg_port = scope.custom_message_port(); + let timer_event_port = &self.timer_event_port; let sel = Select::new(); let mut worker_handle = sel.handle(worker_port); - let mut timer_event_handle = sel.handle(timer_event_port); let mut devtools_handle = sel.handle(devtools_port); - let mut msg_port_handle = sel.handle(msg_port); + let mut timer_port_handle = sel.handle(timer_event_port); unsafe { worker_handle.add(); - timer_event_handle.add(); if scope.from_devtools_sender().is_some() { devtools_handle.add(); } - msg_port_handle.add(); + timer_port_handle.add(); } + let ret = sel.wait(); if ret == worker_handle.id() { Ok(MixedMessage::FromServiceWorker(try!(worker_port.recv()))) - } else if ret == timer_event_handle.id() { - Ok(MixedMessage::FromScheduler(try!(timer_event_port.recv()))) - } else if ret == devtools_handle.id() { + }else if ret == devtools_handle.id() { Ok(MixedMessage::FromDevtools(try!(devtools_port.recv()))) - } else if ret == msg_port_handle.id() { - Ok(MixedMessage::FromNetwork(try!(msg_port.recv()))) + } else if ret == timer_port_handle.id() { + Ok(MixedMessage::FromTimeoutThread(try!(timer_event_port.recv()))) } else { panic!("unexpected select result!") } } - pub fn script_chan(&self) -> Box { - box WorkerThreadWorkerChan { - sender: self.own_sender.clone(), - worker: self.worker.borrow().as_ref().unwrap().clone(), - } - } - pub fn pipeline(&self) -> PipelineId { self.id } @@ -352,15 +277,6 @@ impl ServiceWorkerGlobalScope { pub fn process_event(&self, msg: CommonScriptMsg) { self.handle_script_event(WorkerScriptMsg::Common(msg)); } - - pub fn new_script_pair(&self) -> (Box, Box) { - let (tx, rx) = channel(); - let chan = box SendableWorkerScriptChan { - sender: tx, - worker: self.worker.borrow().as_ref().unwrap().clone(), - }; - (chan, box rx) - } } #[allow(unsafe_code)] diff --git a/components/script/dom/serviceworkerregistration.rs b/components/script/dom/serviceworkerregistration.rs index 358331afffc..e20b7e06c47 100644 --- a/components/script/dom/serviceworkerregistration.rs +++ b/components/script/dom/serviceworkerregistration.rs @@ -11,6 +11,8 @@ use dom::bindings::str::USVString; use dom::eventtarget::EventTarget; use dom::serviceworker::ServiceWorker; use dom::serviceworkercontainer::Controllable; +use dom::workerglobalscope::prepare_workerscope_init; +use script_traits::{WorkerScriptLoadOrigin, ScopeThings}; use url::Url; #[dom_struct] @@ -19,7 +21,7 @@ pub struct ServiceWorkerRegistration { active: Option>, installing: Option>, waiting: Option>, - scope: String, + scope: String } impl ServiceWorkerRegistration { @@ -29,7 +31,7 @@ impl ServiceWorkerRegistration { active: Some(JS::from_ref(active_sw)), installing: None, waiting: None, - scope: scope + scope: scope, } } #[allow(unrooted_must_root)] @@ -37,11 +39,47 @@ impl ServiceWorkerRegistration { script_url: Url, scope: String, container: &Controllable) -> Root { - let active_worker = ServiceWorker::init_service_worker(global, script_url, true); + let active_worker = ServiceWorker::install_serviceworker(global, script_url.clone(), true); active_worker.set_transition_state(ServiceWorkerState::Installed); container.set_controller(&*active_worker.clone()); reflect_dom_object(box ServiceWorkerRegistration::new_inherited(&*active_worker, scope), global, Wrap) } + + pub fn get_installed(&self) -> &ServiceWorker { + self.active.as_ref().unwrap() + } + + pub fn create_scope_things(global: GlobalRef, script_url: Url) -> ScopeThings { + let worker_load_origin = WorkerScriptLoadOrigin { + referrer_url: None, + referrer_policy: None, + pipeline_id: Some(global.pipeline()) + }; + + let worker_id = global.get_next_worker_id(); + let init = prepare_workerscope_init(global, None); + ScopeThings { + script_url: script_url, + pipeline_id: global.pipeline(), + init: init, + worker_load_origin: worker_load_origin, + devtools_chan: global.devtools_chan(), + worker_id: worker_id + } + } +} + +pub fn longest_prefix_match(stored_scope: &Url, potential_match: &Url) -> bool { + if stored_scope.origin() != potential_match.origin() { + return false; + } + let scope_chars = stored_scope.path().chars(); + let matching_chars = potential_match.path().chars(); + if scope_chars.count() > matching_chars.count() { + return false; + } + + stored_scope.path().chars().zip(potential_match.path().chars()).all(|(scope, matched)| scope == matched) } impl ServiceWorkerRegistrationMethods for ServiceWorkerRegistration { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 30ab695f9b7..991e8fbfadc 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -44,10 +44,10 @@ use js::rust::CompileOptionsWrapper; use js::rust::Runtime; use libc; use msg::constellation_msg::{FrameType, LoadData, PanicMsg, PipelineId, SubpageId, WindowSizeType}; +use net_traits::ResourceThreads; use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; use net_traits::storage_thread::StorageType; -use net_traits::{ResourceThreads, CustomResponseSender}; use num_traits::ToPrimitive; use open; use profile_traits::mem; @@ -155,8 +155,6 @@ pub struct Window { image_cache_thread: ImageCacheThread, #[ignore_heap_size_of = "channels are hard"] image_cache_chan: ImageCacheChan, - #[ignore_heap_size_of = "channels are hard"] - custom_message_chan: IpcSender, browsing_context: MutNullableHeap>, performance: MutNullableHeap>, navigation_start: u64, @@ -314,10 +312,6 @@ impl Window { self.image_cache_chan.clone() } - pub fn custom_message_chan(&self) -> IpcSender { - self.custom_message_chan.clone() - } - pub fn get_next_worker_id(&self) -> WorkerId { let worker_id = self.next_worker_id.get(); let WorkerId(id_num) = worker_id; @@ -1605,7 +1599,6 @@ impl Window { history_task_source: HistoryTraversalTaskSource, file_task_source: FileReadingTaskSource, image_cache_chan: ImageCacheChan, - custom_message_chan: IpcSender, image_cache_thread: ImageCacheThread, resource_threads: ResourceThreads, bluetooth_thread: IpcSender, @@ -1641,7 +1634,6 @@ impl Window { history_traversal_task_source: history_task_source, file_reading_task_source: file_task_source, image_cache_chan: image_cache_chan, - custom_message_chan: custom_message_chan, console: Default::default(), crypto: Default::default(), navigator: Default::default(), diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index 2cf1d1c4e46..c318672fb7a 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -2,9 +2,9 @@ * 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 devtools_traits::{ScriptToDevtoolsControlMsg, DevtoolsPageInfo}; +use dom::abstractworker::WorkerScriptMsg; use dom::abstractworker::{SimpleWorkerErrorHandler, SharedRt, WorkerErrorHandler}; -use dom::abstractworker::{WorkerScriptLoadOrigin, WorkerScriptMsg}; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::WorkerBinding; use dom::bindings::codegen::Bindings::WorkerBinding::WorkerMethods; @@ -26,6 +26,7 @@ use ipc_channel::ipc; use js::jsapi::{HandleValue, JSContext, JSAutoCompartment}; use js::jsval::UndefinedValue; use script_thread::Runnable; +use script_traits::WorkerScriptLoadOrigin; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{Sender, channel}; use std::sync::{Arc, Mutex}; @@ -81,21 +82,28 @@ impl Worker { let worker_load_origin = WorkerScriptLoadOrigin { referrer_url: None, referrer_policy: None, - request_source: global.request_source(), pipeline_id: Some(global.pipeline()) }; let (devtools_sender, devtools_receiver) = ipc::channel().unwrap(); + let worker_id = global.get_next_worker_id(); + if let Some(ref chan) = global.devtools_chan() { + let pipeline_id = global.pipeline(); + let title = format!("Worker for {}", worker_url); + let page_info = DevtoolsPageInfo { + title: title, + url: worker_url.clone(), + }; + let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal((pipeline_id, Some(worker_id)), + devtools_sender.clone(), + page_info)); + } - let init = prepare_workerscope_init(global, - "Worker".to_owned(), - worker_url.clone(), - devtools_sender.clone(), - closing); + let init = prepare_workerscope_init(global, Some(devtools_sender)); DedicatedWorkerGlobalScope::run_worker_scope( init, worker_url, global.pipeline(), devtools_receiver, worker.runtime.clone(), worker_ref, - global.script_chan(), sender, receiver, worker_load_origin); + global.script_chan(), sender, receiver, worker_load_origin, closing); Ok(worker) } diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 67b7a54951d..cd8476b49d6 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.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 devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId, DevtoolsPageInfo}; +use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods; use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception}; @@ -19,18 +19,18 @@ use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; use dom::window::{base64_atob, base64_btoa}; use dom::workerlocation::WorkerLocation; use dom::workernavigator::WorkerNavigator; -use ipc_channel::ipc::{self, IpcSender}; -use ipc_channel::router::ROUTER; +use ipc_channel::ipc::IpcSender; use js::jsapi::{HandleValue, JSContext, JSRuntime}; use js::jsval::UndefinedValue; use js::rust::Runtime; use msg::constellation_msg::{PipelineId, ReferrerPolicy, PanicMsg}; use net_traits::{LoadContext, ResourceThreads, load_whole_resource}; -use net_traits::{RequestSource, LoadOrigin, CustomResponseSender, IpcSend}; +use net_traits::{LoadOrigin, IpcSend}; use profile_traits::{mem, time}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, maybe_take_panic_result}; use script_thread::RunnableWrapper; use script_traits::ScriptMsg as ConstellationMsg; +use script_traits::WorkerGlobalScopeInit; use script_traits::{MsDuration, TimerEvent, TimerEventId, TimerEventRequest, TimerSource}; use std::cell::Cell; use std::default::Default; @@ -48,52 +48,19 @@ pub enum WorkerGlobalScopeTypeId { DedicatedWorkerGlobalScope, } -pub struct WorkerGlobalScopeInit { - pub resource_threads: ResourceThreads, - pub mem_profiler_chan: mem::ProfilerChan, - pub time_profiler_chan: time::ProfilerChan, - pub to_devtools_sender: Option>, - pub from_devtools_sender: Option>, - pub constellation_chan: IpcSender, - pub scheduler_chan: IpcSender, - pub panic_chan: IpcSender, - pub worker_id: WorkerId, - pub closing: Arc, -} - pub fn prepare_workerscope_init(global: GlobalRef, - worker_type: String, - worker_url: Url, - devtools_sender: IpcSender, - closing: Arc) -> WorkerGlobalScopeInit { + devtools_sender: Option>) -> WorkerGlobalScopeInit { let worker_id = global.get_next_worker_id(); - let optional_sender = match global.devtools_chan() { - Some(ref chan) => { - let pipeline_id = global.pipeline(); - let title = format!("{} for {}", worker_type, worker_url); - let page_info = DevtoolsPageInfo { - title: title, - url: worker_url, - }; - chan.send(ScriptToDevtoolsControlMsg::NewGlobal((pipeline_id, Some(worker_id)), - devtools_sender.clone(), - page_info)).unwrap(); - Some(devtools_sender) - }, - None => None, - }; - let init = WorkerGlobalScopeInit { resource_threads: global.resource_threads(), mem_profiler_chan: global.mem_profiler_chan().clone(), to_devtools_sender: global.devtools_chan(), time_profiler_chan: global.time_profiler_chan().clone(), - from_devtools_sender: optional_sender, + from_devtools_sender: devtools_sender, constellation_chan: global.constellation_chan().clone(), panic_chan: global.panic_chan().clone(), scheduler_chan: global.scheduler_chan().clone(), - worker_id: worker_id, - closing: closing, + worker_id: worker_id }; init @@ -105,7 +72,7 @@ pub struct WorkerGlobalScope { eventtarget: EventTarget, worker_id: WorkerId, worker_url: Url, - closing: Arc, + closing: Option>, #[ignore_heap_size_of = "Defined in js"] runtime: Runtime, next_worker_id: Cell, @@ -146,12 +113,6 @@ pub struct WorkerGlobalScope { #[ignore_heap_size_of = "Defined in ipc-channel"] panic_chan: IpcSender, - - #[ignore_heap_size_of = "Defined in ipc-channel"] - custom_msg_chan: IpcSender, - - #[ignore_heap_size_of = "Defined in std"] - custom_msg_port: Receiver, } impl WorkerGlobalScope { @@ -159,16 +120,15 @@ impl WorkerGlobalScope { worker_url: Url, runtime: Runtime, from_devtools_receiver: Receiver, - timer_event_chan: IpcSender) + timer_event_chan: IpcSender, + closing: Option>) -> WorkerGlobalScope { - let (msg_chan, msg_port) = ipc::channel().unwrap(); - let custom_msg_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(msg_port); WorkerGlobalScope { eventtarget: EventTarget::new_inherited(), next_worker_id: Cell::new(WorkerId(0)), worker_id: init.worker_id, worker_url: worker_url, - closing: init.closing, + closing: closing, runtime: runtime, resource_threads: init.resource_threads, location: Default::default(), @@ -185,8 +145,6 @@ impl WorkerGlobalScope { constellation_chan: init.constellation_chan, scheduler_chan: init.scheduler_chan, panic_chan: init.panic_chan, - custom_msg_chan: msg_chan, - custom_msg_port: custom_msg_port } } @@ -236,16 +194,12 @@ impl WorkerGlobalScope { self.runtime.cx() } - pub fn custom_message_chan(&self) -> IpcSender { - self.custom_msg_chan.clone() - } - - pub fn custom_message_port(&self) -> &Receiver { - &self.custom_msg_port - } - pub fn is_closing(&self) -> bool { - self.closing.load(Ordering::SeqCst) + if let Some(ref closing) = self.closing { + closing.load(Ordering::SeqCst) + } else { + false + } } pub fn resource_threads(&self) -> &ResourceThreads { @@ -273,7 +227,7 @@ impl WorkerGlobalScope { pub fn get_runnable_wrapper(&self) -> RunnableWrapper { RunnableWrapper { - cancelled: self.closing.clone(), + cancelled: self.closing.clone().unwrap(), } } } @@ -285,9 +239,6 @@ impl LoadOrigin for WorkerGlobalScope { fn referrer_policy(&self) -> Option { None } - fn request_source(&self) -> RequestSource { - RequestSource::None - } fn pipeline_id(&self) -> Option { Some(self.pipeline()) } @@ -451,13 +402,10 @@ impl WorkerGlobalScope { pub fn script_chan(&self) -> Box { let dedicated = self.downcast::(); - let service_worker = self.downcast::(); if let Some(dedicated) = dedicated { return dedicated.script_chan(); - } else if let Some(service_worker) = service_worker { - return service_worker.script_chan(); } else { - panic!("need to implement a sender for SharedWorker") + panic!("need to implement a sender for SharedWorker/ServiceWorker") } } @@ -479,13 +427,10 @@ impl WorkerGlobalScope { pub fn new_script_pair(&self) -> (Box, Box) { let dedicated = self.downcast::(); - let service_worker = self.downcast::(); if let Some(dedicated) = dedicated { return dedicated.new_script_pair(); - } else if let Some(service_worker) = service_worker { - return service_worker.new_script_pair(); } else { - panic!("need to implement a sender for SharedWorker") + panic!("need to implement a sender for SharedWorker/ServiceWorker") } } diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index ed254f8c007..9ac02936673 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -47,7 +47,7 @@ use net_traits::CoreResourceMsg::Fetch; use net_traits::request::{CredentialsMode, Destination, RequestInit, RequestMode}; use net_traits::trim_http_whitespace; use net_traits::{CoreResourceThread, LoadOrigin}; -use net_traits::{FetchResponseListener, Metadata, NetworkError, RequestSource}; +use net_traits::{FetchResponseListener, Metadata, NetworkError}; use network_listener::{NetworkListener, PreInvoke}; use parse::html::{ParseContext, parse_html}; use parse::xml::{self, parse_xml}; @@ -274,13 +274,6 @@ impl LoadOrigin for XMLHttpRequest { fn referrer_policy(&self) -> Option { return self.referrer_policy; } - fn request_source(&self) -> RequestSource { - if self.sync.get() { - RequestSource::None - } else { - self.global().r().request_source() - } - } fn pipeline_id(&self) -> Option { let global = self.global(); Some(global.r().pipeline()) diff --git a/components/script/lib.rs b/components/script/lib.rs index 2744115aff1..604e3770176 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -106,6 +106,7 @@ pub mod parse; pub mod script_runtime; #[allow(unsafe_code)] pub mod script_thread; +mod serviceworker_manager; mod task_source; pub mod textinput; mod timers; @@ -113,7 +114,10 @@ mod unpremultiplytable; mod webdriver_handlers; use dom::bindings::codegen::RegisterBindings; +use ipc_channel::ipc::IpcSender; use js::jsapi::{Handle, JSContext, JSObject, SetDOMProxyInformation}; +use script_traits::SWManagerMsg; +use serviceworker_manager::ServiceWorkerManager; use std::ptr; use util::opts; @@ -159,11 +163,14 @@ fn perform_platform_specific_initialization() { fn perform_platform_specific_initialization() {} #[allow(unsafe_code)] -pub fn init() { +pub fn init(from_swmanager_sender: IpcSender) { unsafe { SetDOMProxyInformation(ptr::null(), 0, Some(script_thread::shadow_check_callback)); } + // Spawn the service worker manager passing the constellation sender + ServiceWorkerManager::spawn_manager(from_swmanager_sender); + // Create the global vtables used by the (generated) DOM // bindings to implement JS proxies. RegisterBindings::RegisterProxyHandlers(); diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 07779fbebd2..57bd85ec1ec 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -65,12 +65,11 @@ use js::jsval::UndefinedValue; use js::rust::Runtime; use mem::heap_size_of_self_and_children; use msg::constellation_msg::{FrameType, LoadData, PanicMsg, PipelineId, PipelineNamespace}; -use msg::constellation_msg::{ReferrerPolicy, SubpageId, WindowSizeType}; -use net_traits::LoadData as NetLoadData; +use msg::constellation_msg::{SubpageId, WindowSizeType, ReferrerPolicy}; use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread}; use net_traits::{AsyncResponseTarget, CoreResourceMsg, LoadConsumer, LoadContext, Metadata, ResourceThreads}; -use net_traits::{RequestSource, CustomResponse, CustomResponseSender, IpcSend}; +use net_traits::{IpcSend, LoadData as NetLoadData}; use network_listener::NetworkListener; use parse::ParserRoot; use parse::html::{ParseContext, parse_html}; @@ -216,8 +215,7 @@ enum MixedMessage { FromScript(MainThreadScriptMsg), FromDevtools(DevtoolScriptControlMsg), FromImageCache(ImageCacheResult), - FromScheduler(TimerEvent), - FromNetwork(IpcSender>), + FromScheduler(TimerEvent) } /// Messages used to control the script event loop @@ -342,12 +340,6 @@ pub struct ScriptThread { /// events in the event queue. chan: MainThreadScriptChan, - /// A handle to network event messages - custom_message_chan: IpcSender, - - /// The port which receives a sender from the network - custom_message_port: Receiver, - dom_manipulation_task_source: DOMManipulationTaskSource, user_interaction_task_source: UserInteractionTaskSource, @@ -509,10 +501,10 @@ impl ScriptThread { } // stores a service worker registration - pub fn set_registration(scope_url: Url, registration:&ServiceWorkerRegistration) { + pub fn set_registration(scope_url: Url, registration:&ServiceWorkerRegistration, pipeline_id: PipelineId) { SCRIPT_THREAD_ROOT.with(|root| { let script_thread = unsafe { &*root.get().unwrap() }; - script_thread.handle_serviceworker_registration(scope_url, registration); + script_thread.handle_serviceworker_registration(scope_url, registration, pipeline_id); }); } @@ -562,9 +554,6 @@ impl ScriptThread { let (ipc_devtools_sender, ipc_devtools_receiver) = ipc::channel().unwrap(); let devtools_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_devtools_receiver); - let (ipc_custom_resp_chan, ipc_custom_resp_port) = ipc::channel().unwrap(); - let custom_msg_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_custom_resp_port); - // Ask the router to proxy IPC messages from the image cache thread to us. let (ipc_image_cache_channel, ipc_image_cache_port) = ipc::channel().unwrap(); let image_cache_port = @@ -590,8 +579,6 @@ impl ScriptThread { bluetooth_thread: state.bluetooth_thread, port: port, - custom_message_chan: ipc_custom_resp_chan, - custom_message_port: custom_msg_port, chan: MainThreadScriptChan(chan.clone()), dom_manipulation_task_source: DOMManipulationTaskSource(chan.clone()), @@ -653,7 +640,7 @@ impl ScriptThread { /// Handle incoming control messages. fn handle_msgs(&self) -> bool { use self::MixedMessage::{FromConstellation, FromDevtools, FromImageCache}; - use self::MixedMessage::{FromScheduler, FromScript, FromNetwork}; + use self::MixedMessage::{FromScheduler, FromScript}; // Handle pending resize events. // Gather them first to avoid a double mut borrow on self. @@ -687,7 +674,6 @@ impl ScriptThread { let mut timer_event_port = sel.handle(&self.timer_event_port); let mut devtools_port = sel.handle(&self.devtools_port); let mut image_cache_port = sel.handle(&self.image_cache_port); - let mut custom_message_port = sel.handle(&self.custom_message_port); unsafe { script_port.add(); control_port.add(); @@ -696,7 +682,6 @@ impl ScriptThread { devtools_port.add(); } image_cache_port.add(); - custom_message_port.add(); } let ret = sel.wait(); if ret == script_port.id() { @@ -709,8 +694,6 @@ impl ScriptThread { FromDevtools(self.devtools_port.recv().unwrap()) } else if ret == image_cache_port.id() { FromImageCache(self.image_cache_port.recv().unwrap()) - } else if ret == custom_message_port.id() { - FromNetwork(self.custom_message_port.recv().unwrap()) } else { panic!("unexpected select result") } @@ -778,10 +761,7 @@ impl ScriptThread { Err(_) => match self.timer_event_port.try_recv() { Err(_) => match self.devtools_port.try_recv() { Err(_) => match self.image_cache_port.try_recv() { - Err(_) => match self.custom_message_port.try_recv() { - Err(_) => break, - Ok(ev) => event = FromNetwork(ev) - }, + Err(_) => break, Ok(ev) => event = FromImageCache(ev), }, Ok(ev) => event = FromDevtools(ev), @@ -807,7 +787,6 @@ impl ScriptThread { }, FromConstellation(inner_msg) => self.handle_msg_from_constellation(inner_msg), FromScript(inner_msg) => self.handle_msg_from_script(inner_msg), - FromNetwork(inner_msg) => self.handle_msg_from_network(inner_msg), FromScheduler(inner_msg) => self.handle_timer_event(inner_msg), FromDevtools(inner_msg) => self.handle_msg_from_devtools(inner_msg), FromImageCache(inner_msg) => self.handle_msg_from_image_cache(inner_msg), @@ -866,8 +845,7 @@ impl ScriptThread { _ => ScriptThreadEventCategory::ScriptEvent } }, - MixedMessage::FromScheduler(_) => ScriptThreadEventCategory::TimerEvent, - MixedMessage::FromNetwork(_) => ScriptThreadEventCategory::NetworkEvent + MixedMessage::FromScheduler(_) => ScriptThreadEventCategory::TimerEvent } } @@ -1056,12 +1034,6 @@ impl ScriptThread { msg.responder.unwrap().respond(msg.image_response); } - fn handle_msg_from_network(&self, msg: IpcSender>) { - // We may detect controlling service workers here - // We send None as default - let _ = msg.send(None); - } - fn handle_webdriver_msg(&self, pipeline_id: PipelineId, msg: WebDriverScriptCommand) { let context = self.root_browsing_context(); match msg { @@ -1457,8 +1429,33 @@ impl ScriptThread { } } - fn handle_serviceworker_registration(&self, scope: Url, registration: &ServiceWorkerRegistration) { - self.registration_map.borrow_mut().insert(scope, JS::from_ref(registration)); + fn handle_serviceworker_registration(&self, + scope: Url, + registration: &ServiceWorkerRegistration, + pipeline_id: PipelineId) { + { + let ref mut reg_ref = *self.registration_map.borrow_mut(); + // according to spec we should replace if an older registration exists for + // same scope otherwise just insert the new one + let _ = reg_ref.remove(&scope); + reg_ref.insert(scope.clone(), JS::from_ref(registration)); + } + + // send ScopeThings to sw-manager + let ref maybe_registration_ref = *self.registration_map.borrow(); + let maybe_registration = match maybe_registration_ref.get(&scope) { + Some(r) => r, + None => return + }; + if let Some(context) = self.root_browsing_context().find(pipeline_id) { + let window = context.active_window(); + let global_ref = GlobalRef::Window(window.r()); + let script_url = maybe_registration.get_installed().get_script_url(); + let scope_things = ServiceWorkerRegistration::create_scope_things(global_ref, script_url); + let _ = self.constellation_chan.send(ConstellationMsg::RegisterServiceWorker(scope_things, scope)); + } else { + warn!("Registration failed for {}", pipeline_id); + } } /// Handles a request for the window title. @@ -1601,7 +1598,6 @@ impl ScriptThread { HistoryTraversalTaskSource(history_sender.clone()), self.file_reading_task_source.clone(), self.image_cache_channel.clone(), - self.custom_message_chan.clone(), self.image_cache_thread.clone(), self.resource_threads.clone(), self.bluetooth_thread.clone(), @@ -2112,8 +2108,7 @@ impl ScriptThread { pipeline_id: Some(id), credentials_flag: true, referrer_policy: load_data.referrer_policy, - referrer_url: load_data.referrer_url, - source: RequestSource::Window(self.custom_message_chan.clone()) + referrer_url: load_data.referrer_url }, LoadConsumer::Listener(response_target), None)).unwrap(); self.incomplete_loads.borrow_mut().push(incomplete); diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs new file mode 100644 index 00000000000..d394a9908ee --- /dev/null +++ b/components/script/serviceworker_manager.rs @@ -0,0 +1,123 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +//! The service worker manager persists the descriptor of any registered service workers. +//! It also stores an active workers map, which holds descriptors of running service workers. +//! If an active service worker timeouts, then it removes the descriptor entry from its +//! active_workers map + +use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg}; +use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; +use dom::serviceworkerregistration::longest_prefix_match; +use ipc_channel::ipc::{self, IpcSender, IpcReceiver}; +use script_traits::{ServiceWorkerMsg, ScopeThings, SWManagerMsg}; +use std::collections::HashMap; +use std::sync::mpsc::channel; +use url::Url; +use util::thread::spawn_named; + +pub struct ServiceWorkerManager { + // map of registered service worker descriptors + registered_workers: HashMap, + // map of active service worker descriptors + active_workers: HashMap, + // own sender to send messages here + own_sender: IpcSender, + // receiver to receive messages from constellation + own_port: IpcReceiver, +} + +impl ServiceWorkerManager { + fn new(own_sender: IpcSender, + from_constellation_receiver: IpcReceiver) -> ServiceWorkerManager { + ServiceWorkerManager { + registered_workers: HashMap::new(), + active_workers: HashMap::new(), + own_sender: own_sender, + own_port: from_constellation_receiver + } + } + + pub fn spawn_manager(from_swmanager_sender: IpcSender) { + let (own_sender, from_constellation_receiver) = ipc::channel().unwrap(); + from_swmanager_sender.send(SWManagerMsg::OwnSender(own_sender.clone())).unwrap(); + spawn_named("ServiceWorkerManager".to_owned(), move || { + ServiceWorkerManager::new(own_sender, from_constellation_receiver).start(); + }); + } + + pub fn prepare_activation(&mut self, load_url: &Url) { + let mut scope_url = None; + for scope in self.registered_workers.keys() { + if longest_prefix_match(&scope, load_url) { + scope_url = Some(scope.clone()); + break; + } + } + + if let Some(ref scope_url) = scope_url { + if self.active_workers.contains_key(&scope_url) { + // do not run the same worker if already active. + warn!("Service worker for {:?} already active", scope_url); + return; + } + let scope_things = self.registered_workers.get(&scope_url); + if let Some(scope_things) = scope_things { + let (sender, receiver) = channel(); + let (devtools_sender, devtools_receiver) = ipc::channel().unwrap(); + if let Some(ref chan) = scope_things.devtools_chan { + let title = format!("ServiceWorker for {}", scope_things.script_url); + let page_info = DevtoolsPageInfo { + title: title, + url: scope_things.script_url.clone(), + }; + let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal((scope_things.pipeline_id, + Some(scope_things.worker_id)), + devtools_sender.clone(), + page_info)); + }; + ServiceWorkerGlobalScope::run_serviceworker_scope(scope_things.clone(), + sender, + receiver, + devtools_receiver, + self.own_sender.clone(), + scope_url.clone()); + // store the worker in active_workers map + self.active_workers.insert(scope_url.clone(), scope_things.clone()); + } else { + warn!("Unable to activate service worker"); + } + } + } + + fn start(&mut self) { + while let Ok(msg) = self.own_port.recv() { + match msg { + ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope) => { + if self.registered_workers.contains_key(&scope) { + warn!("ScopeThings for {:?} already stored in SW-Manager", scope); + } else { + self.registered_workers.insert(scope, scope_things); + } + } + ServiceWorkerMsg::Timeout(scope) => { + if self.active_workers.contains_key(&scope) { + let _ = self.active_workers.remove(&scope); + } else { + warn!("ScopeThings for {:?} is not active", scope); + } + } + ServiceWorkerMsg::ActivateWorker(mediator) => { + self.prepare_activation(&mediator.load_url); + // TODO XXXcreativcoder this net_sender will need to be send to the appropriate service worker + // so that it may do the sending of custom responses. + // For now we just send a None from here itself + let _ = mediator.response_chan.send(None); + + } + ServiceWorkerMsg::Exit => break + } + } + } +} diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 967303e63b8..af3844f84ba 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -36,7 +36,7 @@ mod script_msg; pub mod webdriver_msg; use app_units::Au; -use devtools_traits::ScriptToDevtoolsControlMsg; +use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; use euclid::Size2D; use euclid::length::Length; use euclid::point::Point2D; @@ -51,13 +51,14 @@ use ipc_channel::ipc::{IpcReceiver, IpcSender}; use layers::geometry::DevicePixel; use libc::c_void; use msg::constellation_msg::{FrameId, FrameType, Image, Key, KeyModifiers, KeyState, LoadData}; -use msg::constellation_msg::{NavigationDirection, PanicMsg, PipelineId}; +use msg::constellation_msg::{NavigationDirection, PanicMsg, PipelineId, ReferrerPolicy}; use msg::constellation_msg::{PipelineNamespaceId, SubpageId, WindowSizeType}; -use net_traits::ResourceThreads; use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::ImageCacheThread; use net_traits::response::HttpsState; +use net_traits::{LoadOrigin, ResourceThreads}; use profile_traits::mem; +use profile_traits::time as profile_time; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::collections::HashMap; use std::sync::mpsc::{Sender, Receiver}; @@ -67,6 +68,7 @@ use util::ipc::OptionalOpaqueIpcSender; use webdriver_msg::{LoadStatus, WebDriverScriptCommand}; pub use script_msg::{LayoutMsg, ScriptMsg, EventResult, LogEntry}; +pub use script_msg::{ServiceWorkerMsg, ScopeThings, SWManagerMsg}; /// The address of a node. Layout sends these back. They must be validated via /// `from_untrusted_node_address` before they can be used, because we do not trust layout. @@ -603,3 +605,49 @@ pub enum ConstellationMsg { /// A log entry, with the pipeline id and thread name LogEntry(Option, Option, LogEntry), } + +/// Resources required by workerglobalscopes +#[derive(Serialize, Deserialize, Clone)] +pub struct WorkerGlobalScopeInit { + /// Chan to a resource thread + pub resource_threads: ResourceThreads, + /// Chan to the memory profiler + pub mem_profiler_chan: mem::ProfilerChan, + /// Chan to the time profiler + pub time_profiler_chan: profile_time::ProfilerChan, + /// To devtools sender + pub to_devtools_sender: Option>, + /// From devtools sender + pub from_devtools_sender: Option>, + /// Messages to send to constellation + pub constellation_chan: IpcSender, + /// Message to send to the scheduler + pub scheduler_chan: IpcSender, + /// Sender which sends panic messages + pub panic_chan: IpcSender, + /// The worker id + pub worker_id: WorkerId, +} + +/// Common entities representing a network load origin +#[derive(Deserialize, Serialize, Clone)] +pub struct WorkerScriptLoadOrigin { + /// referrer url + pub referrer_url: Option, + /// the referrer policy which is used + pub referrer_policy: Option, + /// the pipeline id of the entity requesting the load + pub pipeline_id: Option +} + +impl LoadOrigin for WorkerScriptLoadOrigin { + fn referrer_url(&self) -> Option { + self.referrer_url.clone() + } + fn referrer_policy(&self) -> Option { + self.referrer_policy.clone() + } + fn pipeline_id(&self) -> Option { + self.pipeline_id.clone() + } +} diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index e510a74a9c1..959050ee2ea 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -8,13 +8,17 @@ use IFrameLoadInfo; use MouseButton; use MouseEventType; use MozBrowserEvent; +use WorkerGlobalScopeInit; +use WorkerScriptLoadOrigin; use canvas_traits::CanvasMsg; +use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use euclid::point::Point2D; use euclid::size::Size2D; use gfx_traits::LayerId; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData}; use msg::constellation_msg::{NavigationDirection, PipelineId, SubpageId}; +use net_traits::CustomResponseMediator; use offscreen_gl_context::{GLContextAttributes, GLLimits}; use style_traits::cursor::Cursor; use style_traits::viewport::ViewportConstraints; @@ -106,6 +110,8 @@ pub enum ScriptMsg { ActivateDocument(PipelineId), /// Set the document state for a pipeline (used by screenshot / reftests) SetDocumentState(PipelineId, DocumentState), + /// Message from network to constellation + NetworkRequest(CustomResponseMediator), /// Update the pipeline Url, which can change after redirections. SetFinalUrl(PipelineId, Url), /// Check if an alert dialog box should be presented @@ -131,6 +137,45 @@ pub enum ScriptMsg { LogEntry(Option, Option, LogEntry), /// Notifies the constellation that this pipeline has exited. PipelineExited(PipelineId), + /// Store the data required to activate a service worker for the given scope + RegisterServiceWorker(ScopeThings, Url), /// Requests that the compositor shut down. + Exit +} + +/// Entities required to spawn service workers +#[derive(Deserialize, Serialize, Clone)] +pub struct ScopeThings { + /// script resource url + pub script_url: Url, + /// pipeline which requested the activation + pub pipeline_id: PipelineId, + /// network load origin of the resource + pub worker_load_origin: WorkerScriptLoadOrigin, + /// base resources required to create worker global scopes + pub init: WorkerGlobalScopeInit, + /// the port to receive devtools message from + pub devtools_chan: Option>, + /// service worker id + pub worker_id: WorkerId, +} + +/// Messages sent to Service Worker Manager thread +#[derive(Deserialize, Serialize)] +pub enum ServiceWorkerMsg { + /// Message to register the service worker + RegisterServiceWorker(ScopeThings, Url), + /// Message to activate the worker + ActivateWorker(CustomResponseMediator), + /// Timeout message sent by active service workers + Timeout(Url), + /// Exit the service worker manager Exit, } + +/// Messages outgoing from the Service Worker Manager thread +#[derive(Deserialize, Serialize)] +pub enum SWManagerMsg { + /// Provide the constellation with a means of communicating with the Service Worker Manager + OwnSender(IpcSender), +} diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 20aee632c1d..0b8f5756a36 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -83,7 +83,7 @@ use profile::mem as profile_mem; use profile::time as profile_time; use profile_traits::mem; use profile_traits::time; -use script_traits::{ConstellationMsg, ScriptMsg}; +use script_traits::{ConstellationMsg, ScriptMsg, SWManagerMsg}; use std::cmp::max; use std::rc::Rc; use std::sync::mpsc::Sender; @@ -114,7 +114,6 @@ impl Browser where Window: WindowMethods + 'static { // Global configuration options, parsed from the command line. let opts = opts::get(); - script::init(); // Get both endpoints of a special channel for communication between // the client window and the compositor. This channel is unique because @@ -160,13 +159,16 @@ impl Browser where Window: WindowMethods + 'static { // Create the constellation, which maintains the engine // pipelines, including the script and layout threads, as well // as the navigation context. - let constellation_chan = create_constellation(opts.clone(), - compositor_proxy.clone_compositor_proxy(), - time_profiler_chan.clone(), - mem_profiler_chan.clone(), - devtools_chan, - supports_clipboard, - webrender_api_sender.clone()); + let (constellation_chan, swmanager_sender) = create_constellation(opts.clone(), + compositor_proxy.clone_compositor_proxy(), + time_profiler_chan.clone(), + mem_profiler_chan.clone(), + devtools_chan, + supports_clipboard, + webrender_api_sender.clone()); + + // Send the constellation's swmanager sender to service worker manager thread + script::init(swmanager_sender); if cfg!(feature = "webdriver") { if let Some(port) = opts.webdriver_port { @@ -227,7 +229,8 @@ fn create_constellation(opts: opts::Opts, mem_profiler_chan: mem::ProfilerChan, devtools_chan: Option>, supports_clipboard: bool, - webrender_api_sender: Option) -> Sender { + webrender_api_sender: Option) + -> (Sender, IpcSender) { let bluetooth_thread: IpcSender = BluetoothThreadFactory::new(); let (public_resource_threads, private_resource_threads) = @@ -252,7 +255,7 @@ fn create_constellation(opts: opts::Opts, supports_clipboard: supports_clipboard, webrender_api_sender: webrender_api_sender, }; - let constellation_chan = + let (constellation_chan, from_swmanager_sender) = Constellation::::start(initial_state); @@ -261,7 +264,7 @@ fn create_constellation(opts: opts::Opts, constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap(); }; - constellation_chan + (constellation_chan, from_swmanager_sender) } // A logger that logs to two downstream loggers. @@ -308,7 +311,9 @@ pub fn run_content_process(token: String) { create_sandbox(); } - script::init(); + // Send the constellation sender to service worker manager thread + let from_swmanager_sender = unprivileged_content.swmanager_chan(); + script::init(from_swmanager_sender); unprivileged_content.start_all::hWG11%L(0TS&dN`DTZoDEF6A8&8ZJp%UfJ7%viI)Y%K&>U=K`bf zfK&RYQ2MJyQj~N8uj3TPc;<_x7hHSEC91>b=M(%eMZSE?+;6ylaX};#w z^W(Mf;WG8&xAGIUdCLVefy3eI>guWp6&-C&JsoX*eSKr3p}Dy^(#XK-skybK#oL#r zHa0f)w$B|M9Ubg#-CSK9UU|5?yZih5`+9r2zqAk1q>0d_i8rK8LDHt1(#0Dw=2_4e zTG4;BVW_ZWtgvUTvSX~VXDoejFUd(R)I~est$*nsAPDuA&me%`I9R|eT=Z$Av}4di zJCu-RtcZ1jsBM~L_|b*C?+mCG9fNHAtB*?a$?5&l#GmwoUHW1ylix4aYSxeYH3YeQDuI9eo0aO z$Kt}$($eaRk9E}*#q}-K)zvKxb#2WJpIV!0+CFu4v|_tE>N~nxx;_u~ejXX@#SRP% z^!HAV4$XWSTbP+#S(urbo*Ww=U0YdP|F*KZ{%vPt?dR_1&%Ldq{q57^gVU4ai_@cv zi{IDRSO3J(|1X<67ho{)U(N5}2)w;)Je*yDJAX*Dl=Pn{%IheJ3JT-n{r$-QAJhIb zVE`x&@E~GARONI9L23BR`m1t3hfv;A%Rp4;^+Yf}a9HZE&hLw1R}5!Sttl8t;CWJN zHc(SIlp_3WFhjMrXe3?Y_0G~j?T4{!SP(I*T3zu(J|cn7e6X(M%Lko&wM@18(&>-L zT8HJq`j4}fmY>5})f>v@YwbyTiF587B}p>+@XAc6&K9t?cn?Hfd9JU7mQ%Vr6~Z4k1zxT!7Q_pg00HcNg#Jn5FII&rTi7>vUX>5g{VTOCx-J2>^hQLh9br6QH z;5h)&n|jOW?fNaUxU~^t zs5{AQd0-Ze(bQc?ik}&QCI$K-dmbkgoNMoM{%FnHyunO9P=1GX$`pP>=T2gEOZEuP z-?o$u+14p^(sABR*c9)lRxiDse(0y((TE4%Zqt1w8Vt za~-e;9Y=&KRp&!hJSYsNM2{z7DbEqIecZ{y6{&;qHJ1SJIDomlAx@mPROh^+q?!t= zP;)3A9!S5bCAHWp0|ugl6xr`p_j7&RtLcyaJ?oG;BC?2IysoE0Bq1f04P-5essJ7W z1kP|HiCY7x7O@f%9`dP~P~uX$*a&`l8A>5IUzPn(3@GjpB3MAHQ_e|M<)%(I$7DiC zG+hq@7#iL6I%#S;^I&|91=NFWd{bQ!pRMYY<^)NMHsSqE!oPKBIgxtHFkaU<5`GS| zco)o2vjobF%Iy6RQNrAFJhX0I>EHkQLf&)5@%jv($z~pL|D%E;cXz|Bj5UYbA<$a37viVV}^S>yz1cdPE*r>WH-OrH%^VdyIHQY?Q<% z^78MhbZ8jP?!{6=$$e>?vDMr)Us|Jvpp>}B z3nL^Y_~CLpH#t*Snf{mc)NLEXCU zV9|)<_9G)I_5?wxBRTlOudH+2@CUh`_y{FFaI%ICN#rUh9GbI6-%tgwrUzvnFDW=y z!2o_YDgyHU#l5}ox;10H%W+z_{tj|tjnvvspMOtP_ zs6N+y36G16#6DH%QBp6?0jQ8{>anX}L!B;sk0S^n?dk`LTzS0F3vqMgniEBA zS@DbyNVnry6dQ&^!z`-tAqZFY*C-TzMo!Vn;aythy%!WT_aNU36d>ix3Zil6Lmtk; zBvh0M_n4^(sNKT}_|lc=WE^i9CIWavt#J3-c*<^IrZZ&Ui?IPlMCO4Zv9z93q_caLLvga9xc|$@IB1)ZY)S@x( zhd>kAxU|{f4e{2C5!dM2j7^PAsl|&?@6p<<-@}`7R~KU_^17UPjR<+>%Q1{xT^_@m zE%<}WiCDwh{Ck?)YNnTysiSp86y_0{ewSZdvXuB>nmc-%&GZQ~w==hB0|?75r>lw* zK0dqIfsS9!P*t6ldyVW`TwTuA5S&$d2<#!pZq4+)^{9!^+ZN*liN~FUvIwpOg5E|T% z3&4xC1U@tHsQ3sazHyCez5XE_(>!da^($@hdP8EYdDLt4SJu__CXAwGJVfi{{btJ+ zqSAXZh5SVCT49~*vCrU}E|RChH#^9&mYK%U(+a|)dp_bZ2@eM?3Aax$p(xU;{Tj+nyhODpcQDyDtUPWx(M5qDNa zFC*Ll!|*!c{dn3Sbr7O`y~h0KykEQHIOVBJS5N@)_y+~XqS5Oe(?6GUV;yIWxUo0Y z2@30pp>ii5Rzoe?+OM{>KV8m_;m%s0UKMjoejEFPyIlN(J0JUm`z_?PoN&#+J4=$< z_YRCi5qP7ZsVHKk(-9$xZ`PNDF@RbkfEF1*AL@rwaqDpPVVDbGI}c!I47`i*%C^+x z3MDS7A}+Qh=C>!lcOEFr7$hnYBzEpr7)$aqF6d{q;lsHgx$__xBbt@RxiBY4xj#rD z6|L5U)|f*d8nG!i1vRds^^q7uZwzwFM~M-mmx{4C$5=83TO&P9`!O3Lm}jZM4o$&M zbHNXcg3UtFE)pT`$PiEakb<0twmczTb0KffLr}%&LICqBG&sOJ6c?Hr8jcKbWyJV5 zg~l?5#Y=>3&xWelhbE?mr8R|RApIWM2iwhs79kDBzpdq?lhMIX!sJv!IfJ&!&?#+>zs zQgBA6c*k7N#o(IW&Cg-}NW_ATV;31>52~XGnqx`lW2?EN%p^#fR%2<6xUq%K^< z2O^~}5@1Y;pMvABcoLO;5)s>gka42we4>_Qg6u`2o@5eNb7JdOg8us?ljc~sPm<|H zlBH_wwnUVbakA}2v|&jU(kIzzK6#2U$%QG!Z9CFcGR4y;r9Cwn6qe#YpE6M#6Wx^j zRx%aM6!r~~jCr3LUK09DGA808HTFe_@B7$5ruWG{?=6rism<>*d@xZ=shLb^c?0MK z-iUmkv|`nuwD)Oc^J#2NX%$TAH6^GcXsUx`dei%K0>$+K@5$m23#RNdpX`v7xOSg}^Z9I?s*lN+^u&v7ut|>nYzAm2i=ZWk zw8f);K8NyF?nBAQB;H(l-&}E{wCk{3mW5pCY|@YI+`CeFS7$jKzIi-SiL_~X{0n(} zY2kD`d7{2~TvGYse0ke*S$BN%Wu)?oQ*)RX@)ZVi9!eD`JLD+&7N{*`tF{zq5oc>& z7U*4O>Pi(F@?}EbXBxE>9vK&!T^4@YF0_;?s^KlN@hvKOQDomzlv`5dbXk$=F&QSDe*1 zSj|3^dW*S+<0^#Aw1#IfBp@ZnZVOk%dsXvZvPM|C_Jw`5m|rc4eXT@mZ7XlB%vG&c zaV<=`?u@$*?pL>y;;0-^r!n*94mHjsFi`L(v(f8Dm7+IrRM&k%hsmD6e3=4XW7fu{Ag zwt0oL!FFnfncD@8TPIE1$#H7!GwJObDXj~O?bW^QE6g2gVeMpEyHl4ky)2vTaVacj|FM1B}*@_bgzwBjjez0o%CLZ;cBO)UdF3lSB+{nnLbL> zKF_u)?~Fb|lDv+AKHSfU92(O8jor2&js7*}tdo+SP_zE~r)^Oz{W1Riab@k@>H|qm z?S2^pr!4~+ER|W;1N*lI^D-(5%m&we2a8E7O4|mP76vOK%d1$1rlf}INXv~gDjQ`! zt+x&pISzG1mNmEKcMX4PHyh4OFO&}-Ch;F0_HXXI9&WfbGD-Sz%50?6cVy0~bfIk| zZ((F*sbrO9^u5&Rx>?DF|7cv==+08{-qL8u<>;YI@v+R5rAfpCk&;A-wjXPcN%AR zu4k5=)H0dmD#sPDX}0mQj^ACL)VQ7$zQKyhe$g}gB5vLzk@-bBVp7IAU+(6M=&dP* z@#P)+Gd=}`&@2j-muKN$yTYE&%)Jaz4t4<+Gio*V{b=l-wsa(2+ZMy4`9rD zO`_()1f~lX=N>H1#yV%CWaovX=ggevc}(ZiqI&t$=QG=TZOmtKHETi!<_cx2O}(c} z_UFo$7aZH>EXfvK{O4*l7hmx&Mn*0&46&^HEVh*|%!e&>2~-7zW@62ka68r2AD1Xw zCv*0f2yjF5ld@F-jnioJW#WkWp|a&1Gq%e8o|+jl>y zHL_i$|YC6zq$Ahi%wCHTlX|xR~Ae;&0J^1ed8WoPYz$#8BM;B{qdn? z-Ej0{cJrcfN2$Tek1W0)mK{l(w^pC66vynY+jW$x-T&rz@Z$k)LofV?+vtdc#pV*r zrgudF&bnh$^Ww+b(KQyft=+Os^l1Kb@}-dI;;_*l5n9FJ3!5<=2{Dda335}(Z?}~* zx6`twyjHeD`L=U9N;2ej`IHwNBLcCYN=!uG~Tlg8xsfAj9o2qsTw?d!DcFGXh! zUax@h#yCHQm1^782J zS6teq#W8#3Ax}*H#n#b%?E;h9X?EN(R_j-M?r{BQKTkl=Uc z9;xTyk3%BAo;#nP$Hr6zJ-#sJy-0o>oSJ<>YI>3Jr#S1+#XGjkyqLuFrjzEO z!#Ii{o9yMWm&eVIZw!oYX0+o+vv2M+-7NhH@Ug%88uNbTF^+)|ck3lC!x*=t9kBNm zM}3Yv%&wh#{D(XYC;0Nu8qwX3!oKC<3 zo2vW0SFhRi4VdY8raX#MGYkG+?}u8lfO!X+>B)vh+C%3R?E5F1T3NDhaNt||Tbg=z zqTaGd>Tl~6X=ch{ZW-+8ml~8?^rRW=8kQHxutDA@?jmcRk7OyO8}6GlI4^f#h8k#* zaU9=QdeV)4T6Da*!9iG%!ZXe8aSN;&$Rq3CDAvcpEXKz+;YwuRsnv~t*^Xw)zQYl) znw;1NV#w^cGEGh$X3EV!1+%8-y=W`)w@jWXY3Q=0J!1-UE?ti^~g%f^@ zySe#8DBPKN@|j-_j{+_%4`Hy0>D*^T3o8(D7YK_Jl9dT7l090lQtT2u5r$K#a8$#) z1@Di;>5Ocul^D!HM@md~1=Y&T&+0@}SiRPDyY*a}_qy0ZIBF30K7T$!aHiPQsD6)k z7ggi^P(Url^ARhmF3>o&8$ejGE~+8Y$MLK5b`Z8o%>rEQU5Q=n z18v#uw%6K^|A58RhqTlm=qOSs)gda!HR^O#ZkJbUBe)CeCZU}5Vox+5)mP@5cDPmO zXsK}4>mQAR4WxB7pVu20C44?HFt#hKuNTs#tuZwB`jJ$N^n-{at^VxrAWVam#EqX{ zNF|Iv&&G%^)V@t>FmbHhFPUF3>}fE4`6;RdY1hTsX!hD*hbKjUM7q)3>uc0jpLQdr z(ZcU1U8srw?;nqh-&{c?R!s;CjVyyG$j_dl8Em~LZ_hq&v07Grq_Y1t$`1YKx&KL^Z;p} zA45K2{r5oB@&fuMdhSKZ2LfTc(#qaTsgJcAEsn^5wmHY@zL{HV#RJOH&d67%Nax0{ z!BO^U--=pY=5vC_T-two#yEHUA!w_0@5+*P{Y>$4&#LwbyzNyVUiy88?ssq7+=hPg zIUSh^ZMMDMn<86$J)!dG^>MeVitOo!2E%rbS!3%Pk9oT>9?ylBeK&U&UvIYm4)pvj zd%hIF(BVCQv@+)XBZd3r(fa!kHs8XC=nmf>adu0-`){<3el;`_X1i;*Q+)DY_`&(r z|J3xh|MuzE-`7@Oeg%KRjcPKzVEDbidHdDgOG(2&;LxTg=gufXu-O%^I0Qzpp!w>` z(GonNEJI{iWlLLUNnj8xPv$9X!*XH?6(LZdN{+R}RWsr)Z+u+Ll!>&d`$?H_bArvS1J%sl_FTvExul$Qx&+ZXHdO(!Bm$D4;pQ z;yf>PY)OyLy;=Ra;FtH~SgxdK)sd&5FX^+xwi4-eBQ~C27~19_5*=e6d&Da>$)5o^9?~MsYx=W#PlODm#TqU?pw9Gv<~|%wdXB47~F%x2R*!j&_T`D zMNbMOA$%x@(ur__>EhG*ZPj+BFZtpga*E~-x|vc2C$Q6uM}-cC6VLP<91=@RAiG|o zJDUFF56bLLwbaekW-9Zh%O_QL)PG$Vwy1q5l1*|jKUKqJWP+Zh!2f38Ag$@ne5V=C ztWMUipCFsN^{Zo+qj=>?HT>_=*Q7ird*&Q&FzlK9(BUDE@B|ZbEP3XE>S^Jl^a0~9 z4QCH9=DW(~(xzkHcausGKlnAPiH+Bi8$K;-@N)T$Spc(5@l+M+e+DAf*k+rTi>cya1;x#@J1R@8{N40Ud1n2qO98VQxzCT=OYeEXFFc{ z0G?jWm?IbY`qTE8s9A;Z7~25iPN$;hkT%PjDu#kD5aA%sVLHMf_j;OM*Pxqu%}YK; zVTx6KF#X-NC^$Qah@Cu)CeIpOm3oIuS;QWu@gB#-UC{fYS;SZjMk$2Z?3FZ(h#=Ja z=5ThmNZJz?1JQfNMFH;TlRu8nZi~OI-OQ^Nte%kI^t=<=%+4=@j55i0DuK+Y1;-@&e2X5$P?T@UNlW(W zde$Xyi$3F@Wt`MY7PEL8X)E$l2@h&jY1Lj5VZPNp^+DOA)S%tLu(4&T=&i@c=d!!{ zpQL8$dh5%aGaW6mFJ}sZPs_Ymcc0R<&bA3QR3^P~vaR%G7{fiR$dB54@n~_OsRJTp zlj&^X?#Hp9^-##g%Hd@MbLRNlv-;_F7f)Q{;&S;$L*sXW1E+kG#kIGd?HICsuiq^T zHw5RUuj6(CcJo$uM7=v5+g&a5eP_ujylbw^UCo6|zcD;|*dx;MD%xx5ds(t~Khw&~ zuy+{@w^Td?Tby0oJ^dKw--qwwAGo}G%{!gPmcL*??e z!8^ajv4b$bU4;!#?nf&}7Qfp!Zw?mDe7-{7{Y?9{e7M|jHAwO47<(VLB=@KFE9YbH zcILm?&KQ{=4?g(}Yp)z<5VmjrX1naThg&&{dL-NYGV6Ts%4{o}L3Z2frI-EhZW^b* zdD)VztEvRq-A_iBKXpa?^|w~e`&4lA=@lN;_ny*eXusvP4)gdJ^JY(-P>!!D%YQdT z_EN`7>eok#x8;w`uT?g0eveU{{cQYt^JGl+WJLSz>Vw-?n%YWp30dzhLT>-rvXjD* ze7eS$)?@E%RD_*ewVdF8%9n=dr9zBjJBbQ9iR(H^u$`n+on-5sGtw}qz~htFL5*yQO@azn7l{F&)%nQlR9Rp=@5)Yw6w}g6_A3_uJO>1l!0s zwIPCRl%n*N!`u49{gr20l`(C-L1u_ZnE{lI^5Vz7x9$Te>u_V#!21H#H~K0**nx~G zRnNBWO#MfC_5*3OgSi}od18Y)`aNObfdGD0I}OzYxNMw8|J1X7`;(qtKZF%adz_f6 zF;=a3Pc2>_QPL*aW2zi~qLyT%_Ka55E>fwcpuhYY(VT&3*b{e_8EnJ$%Fm5k0++1SnA7$&j5S9-)g1I=aMV+cdtHkU*q^ziA%xdn9k zYH@**TMbf_&_7EP31!2hv_&^3y4)kZYi+viXp9sv%5xrwa0hfk;pCZc=rsJ%G@Oj} zOAqO=hm#^V%YmSy*nLZ)K=u22`~$uw*d0 zq}Ru6Xc3A5Pz(6`llq~kkLF{J^`;+sG}>6lpN%Zom>X~wnz;TLvoRlWQ(8!=gCM;@ z_>2KU?0CkOI;P%e0EU7V2O4{$r20{y&_HArM5GwBiiY3-6vWgUC0PYPiUX0USY%V6 z;ko9j6GrHSS1sygg~phL+TN#IcU}`hyVaUQ7Bc*Nd#rJ zN=_wf4tQB3z`}A;B@}=H);7-}eC;?(K`20o0#tSIfJ1-;0U@-Uv8aOJRsF4=fe5Qm z2molU4V21R1gcQ?fp4?~h6mv@;^J1ca^I-MH6_m$^fRX=H&!2so7r$0z-$MIf>n)E z1Gz#m`hP2aRexJx4ILiQhQ3`|#u(bNev8OL5tYbl57Y7~DpL4b@> zF%HpKxXzq0P6ER@z5*ZDl0E#&dHB`16YYol@v3Zntgo&Sx@1^oGITOb+Z| z(FpThy2zeBdRorbin*D%WZE>=X^xc(nf_qi)Rbu{svHwRTXRk z8PC~F-q=h8i$6-vVmvT7p%9jh+Mk0vjV6dv9-yP##Zk z?jE}?q@%j*=yfynUiqBtT+kk79x)7lOAm4{f}dfqO5;I@r65!X+!Tr72*qj^KQ%%G zx<%{%eL>Vsm?9u=EWm-{=mWFBW4bNI9w*C=DcKHt`5Bk;j_)$ITL))5=!PxIhG(QJ z`g82=Ki!`44Du&w|?V|p?Bumf28$t+sNzb(#j&nU0|9Ontp?Z828f-5mXJDlH z^H9*SLTPDNu5VHkvYG*=wqFO%M|dcM*8Ne?OiU;JvKHGOJNKv#B@Xzep@rZW)tbAD zh=d4M0fgtfrpb=RP<-B;qc>A~qXfT%2`6CA!zl9&thmYq*Qk^7EZh>k+J;G;?}if*6Ldr)*KHD0CP7O z1Ef}{cn9j<-XXY+(&7xz;W0Jag3uiPv}l3=I+CSDrg|x7FMgj;e(-$q;mqml3DYNc zH+~ef9wQlQroZ~iG!~>vphafB#{>1^0Y>!!K*k@{jcdSscKNcEInG}MRzn|ld!oq5 zmWTH}iIpLnw7-H>ytg=B_ky(^KK}LE-`kkd#8?8u(QjeE^VQ_g&KP)lWB~dp`_;I~ z*0fVGdV17^-A+@&PBjq{pM;fQ0OgNtpw8w~rt$adCXA6M6560YBVbN@%blW=02LMO zbBNv}52v3e4niKz+KyUAmrfxsFKka)JGsq9UV>K_SX0p!s6a$cr7Gp&!^t`5gV|NL?_0#@DId=+xKN+AQuXv zk6dRZd@jIfzBZ+^N{i1Mi_t=%zO)AdIS|~`$Pb!TkgD0z{l8N*3PUaN1JQ>5dv8F$ zAEg?LA%=_!Jiaa%nO@gCUz)w~DCp~pv>o?2f6TrAAO&K`4(5Q(?M|mKs`|Huzgc0ye(Gv2qw)Ld`sMfh**~~V6C7cv1&ENEpC^_fOsX9LS)GiSjDFg#r9;j55rwso=08oN zDNL!L;r8tT#VQ$ngys@Aq&+(X_gOT9`fu5j1YnXVRIr1b5LktU(ayD6QkSK25GaAq zdFO;zzfdDzAye&iTS&XX_SHUa=k)$um33ddfa4keLYVAisU6-!&*@iDn zl^WH$?5Ex5vuJjosBpd#KUiw<`gO2>_2_W5JA_8a5ivFQmqMhm+iC`W}tj|1j+@Pb_I;ltN@X#G&73L6J-sBat->X_-ak+C;Yxdvr8+1u>v6%A(U37Y$e+jajX1-)IS`J_+sl2P z6+paT@}}d^nVyga&7Ri2t`U}(j#;+!e`+DPU>H^KH!Rd)Dt7)bcjm5*0>O2Hf`T~s$o>wj% z*A*Xs*6*BItK;yhD8Xt%G!}TSEo->Nil#|%QX%=xB1aMIp92!%7G_hHiqFi_83r~( z4W6X~H7M%)ouXjwT8ne#a`>M+;bznbTA-z$#pY_4=43qKjhiz-Q#FH3` zjtXpo&%O~nj zamQCCUFOIIIyg<4Pc8E}M zmudBAAfrq??qMLHE3N75W3YP4bBJJ1=k~^`2 z*ySpc5+8+>-Or;+Xi{Was0yZSyF;Y1CC}PR8KPd0OQ(p0^RdODKk_>~4*`0Dw6 zYZ+?lq%B3mIl`wi;mOS3w|Z*^UPZs+XJ#!iQjs`_i}7j8XZ>-m;&mDqgOTB2xi_OC z!-ms~3npE^x0~80Pc0hnXTx!qw?q~0b{yk=0_St1f)`ZBh6Y;QnJi6Ye85%Y;Vi{T zV)UX;w8%A(&;5>I3C$3TTwI95HK%Y;Nv|I0K}1K!8nqvjqLpaEd#sZ!b>)|Ct`YL6 zs)8RBFTzHQt3dP=*IW{xd9}VD-VH2fhwzSuje3+m$=vm~<5de7^=?SW-1g^|S`O3J z!@kZcgh62Vd^-AQ>l^}E9@y8Cu{RY@vVQ%&a`>HhB22F?kG`EpncE>eGTAzx`Ukhl zcC)s}8BM`i8@J+}v`ODA_grGu5;fg4;^e%OjGYW#UcI>RjN`fwB~V_aiFr*u8$DcX zBLDh4xETRsn~slnvf+#nhxpdNgQEW&>yHD}f)oPXqYox~LoSm^m>j-(L`8MU!bux9kxN zetLdVb-CAKFCO2IAbx^Kt`1Y;wqs^q=utDI%R1$MLFG6xrIuV@lDQ@MfKSKZJgGKg zdC%UN#00I@kpBHzz%@u(OE~U@c+W#YXSV<|bDl@dL+w3o>3+55OTjJO^GbrAR_04i zMvoTvD_pHSB9Ue15>^W)c>t z@-=1~@sA!&){g`bWh^a_Hg)wXRRr~bEe;GHwb9c*34BGhytW$H)pmY}QRH9y8;=_J z8zW&#W#;>QcHNu=cK6l35o!Bd_lkFjM87Wk&ibpVPYx#%8$cSaHSFCle+G_=k@>FW z4x*K)sE&Fpvd;TRazL)4Ch4Qha}J8;0X3Xx>Sz89kp!P!Nr-BhURyfhgYzDjx3#Y9 zWlO)hn@2r69%TJ0+mxLR9<2pcXWnSA$oGYh`^$;t(Ufl~J!qMzAcg{enH&%vp!@&9 zc6xN@vB4`-n~LK z6>fjx>!~L^LuwrUo=|k{b`IO)jL9j>56|#rZZlb)GV)M>vy^EJ=dCF*`1$ZVT5)2$ z>Okl-20gVw;R6NAM-Z~}FDmSKY_v)WBT%%;N;_-Jbu;pOZMw|#@rn;hbWgrDnW)I) zoO1{Tm{xRhex!V%$ZLM+8{6!mMDQ|&d4bys)H8~utPYrHP%>xgmprIn-ay%=Cd))kc&yu=~Cqs zgRbXt@cA>Wc>fTfrPCyZ{x(taB_c#X!NRU^rFX8vogg9d;1I(%!or;ZsWAJK7<_y7 zC3e!2PI-J~%K`(XH%`JRicqpH05Bla14(GL2pPWJ1IznbvTWb zSiC5SlOn6wz#xM)Cwp1SrJ6h7Ka7U}F=;BQ_wZw5ldGC` z(v}NIt0gC?Zt06XQ(~^Vkf#VZk?*vYDdXx-&^e;YSBf&m22cdCh@dn`S0VjCK)t$Y zIt+Y_4@6io+bV50T*$L~k-BaqxGD?;uzRx?Q9Yp-(M^=Mj5VR;)FfT+HF~>@)@iUC~m8C-PK|M(ZJe2~6FUO3c^Nh)%cTsMK_lL*3?Y4)188 zIfQSVV_$&cga{4h+?MKu`ob*$1?KkH2_4o1FuNDsVOY5ArROeIZ0Lj_iIjApA!an)on&V zcb*pwRRGi6?;j#|U>ETSx-Si$tP*t17igw2V zu4qD?L(Hk`U5xuTxZdFdS`ryY`FtG|N%cMU!)+(qhkn+I=e=|hhwSf=e3sw)e}~|? zV$JCS!rAw1sFsjm*8nD#!~<-==$nbV5zoTEUp@@&7uk5^yr9-CRz(M3OI(AtV4G6Z zU|A*(NXlKjEff@`tdsjJgPLFzKm~5~^ikC)8V}wQ2@9~_(Uw2aC@%HP$~1^4#$!Yg z!8XMLZ0=!7^z=}F*9JCD`hiW0aWlFBFp`nI> zx-2i*wRA=m?~G5VrotXayQ1kQ6Q zRsow@XmL1D9K&tlJr(VCSC0_yJL2u?BasV;FkSNm2-=C~I$5G>RYOK02fIhkZ;2;* zgii<^M+xI?mwT=8Dc6Vrrvl3OPX{M5LEj8H2et!W?I_}l!?;I8P+BBSG%CSB@-T9S z#~Ao*+~PT(4CrwL0LA()HK=3AT`P)&MSD9v@OS;9+N$T$sx&6tVbML}oaS_4x9OWr z7?WN$GBtm;4y_tpq3#385Pfq_8CsgV^}Cs^IbJ35aHd9qfrA`TAq)cjv!)RIdrc9- zy9JN|M1R+mhX6)3_k>7~$O=kB?AW~x2GbJK-F1R1<@Km)^Q#pU>wZfJCTI7|&?~XT zM)N$JG#-f8g8hR@@^8$CcnknJK>8O`AHWdNo`+#OgP@O-VEsC&-ND^j8kcp2mWqT) z#jrWiLH5to+3Zke5r;c}i!B76PQ`0q@U1A-EXtHLYQ~YZ&`9Bsb0GPPHtHl%g zzZP?Wzx3dNX^HOz<0;}0FfZG2SfB-O2!1t1Pn*RuJKY?@;X>rhUaU2cccMCt^xC<94rRG%2sIjM2 zrs_t7MK{1uFvXsMfDAcFzE?Fx3WO4`rE)&ka%ZCc@iODT#pwQTF%SNF`J-4NHMcN` zhOcbh9$iIEe9thRx_S*pOV4RNoLV)h9L*pfuCbLcaDuF!Le>t#E5<+wq zH1Io>M*qWsZ8x#C)3#CykdmS@T;;BMjxoGT)u5R3LDlaXbzd(iGn(ws-zF!e;tMR; zC9CAJ2=l%?l;(beDna*b9nnW8kKwq6Rm29_6}G?`a1qO z3?*PBhtoD}YRF9#B=h57bxWf_!QR5076WSkW(Act;z_{QW9_{ z=CH@oCiyF<5(AMsMBLf>dPI1DMBF`NAXaAp_Ei7Qco=Ws57@nIgkB)*p2RT`T`I5e zGfr`0co>afu&%;Ad^&a_Ku_m*fI|i(Jdgyqf&yY-#H#CwFDH$O=rZ&bf?)n?c?vOq zFGQ^?yiUTxPzMHyCMXmd2H>mcpAZt_^N$xhc^^&_;^UKc0{94LDHH)7c{hv#$0BA4 zR`xfT>Lb8EcU`0;1zcy@^QkTQ@LxUz;D3AT#=Q*UAMax)yRHK8YfWhT^XV=ou2mi?X65Ff&NJaLtoHc{jY6Ajd|ljJ3%Wu+Pj%N~HoV7B^` zS%ihM6HlVRy_1BNg;G(CNNDMh`8k3T#vMB^TK&i=rBx^fc_0ePn?K_BqqBc3rZ|oKuD#27xE#*ApmaI|*nnCY+w) zBPm+8!0e}SEDuH;JFP_PJG+@3V!!ngi|Ts^YX_-qQ-C7yqAjkI_@%HS zz=D2!1A&pb#KK7D4ncr>8HgXL$ke+jOjOWCw*E|&N#;T9Eq5NS2e1hF&e-}uojfA^ zSDicr?6E=;x$GIKOzAtyl&XK@$QU|CGy?{xsnGx~zn=-?%8mxp68tRTi;sm`9HbJzIOTT-@dMW%R_i+(>6+^U`Gg490TQ`x$|pJ z1iUJ>i-jQfC3`v~*Ba}kv6E7tLy<_d>4QKx8rPO3Z=T+klTwS^HKhK#JSpp5VA}R` zXLP$|N!sg8gsKhQk$Ofl(MFOgygfB?$<9ufz+mq3?J*Dzw+G_r#yxQqr#KTyjIj@+iu%d@Rq(J7J2SV0EAlk#vdU}a7%D*I(E3cQ; zq3~SDqwYa1eIeRPtI-y=KdlI7gfi7QwsgsnA(3o-0BQ z`VQJ9*2*9kkz&PPe*Ww6m~Tq#OCk|CfZPpFu?T}g?pTxLJ}ieU?SaZ0Fy*g(SbAK8 zXAjx>;OK*#Qu{|fmx_GCA?8XM_8aNZkN|DcgFBBQ^3x&ip)=;GjlvC-*1!e)3KR(J zMd@C+%NVFoT(oKsBY%OUq74Fj+x+#DzZTpc2%u0;R(uBbV-FofkBBk^G7hqcvM zfrc!1oak{lX=Rhc6v~NWRULx%V3a!)w`+ME!W2Ban=?RwzqFBMi8Qh%O;#jn9FucM zx$YA5pz<(5RMPQh>-q4X3FT&iau7E^oX%Rb0*nVBlse^=kfF+s9zxC9w#SM;LAn=w zLVma!n;~~`a^i&#QBHS019du7c`HHgJ@>#pv0INpr0H?Hg@Y2ZTAY-KoQFxYyp9{X z)nkHsXi%+Qz!lwAQ-d7CyE1d~Rr&zBxivgy6@UvJ#1ss})<^aM9-R-@>rzD&7E5Bt zo7(zhwufS?YcGNepO%~ss*`X5_H7M_&s<-^1&;&|!mmVz%AhKDPz!QeetAg{1nkGK za>L;l{%lW#F*_5A_NH4R9yZC3^?68 zpuhzCMZ6Wm*^);(`|LOTJR_E~4cVjlAOK?1_tp#wuZ3wx&$->HjS#u9hn4}Pq|QqV zU6D|w(y)j>GN)<^;hf6|>>w;&E9>VC6tq~nPYr{Vn=ytFu)(poS4D)i^15nM@|%es zxpgU1RygYk!b_`tS_Hg1nqSnz(VoZEQ?PqO0~DTPPQ?2f1Yma5YIBL3X#rvy604w3 zxNt?NLLf!P+LGp_95&8O+bscjlw!_FdXCCbSY zA(sT{=4QGfBqb>L3?IaQ^55gXt58#j90Vj+kO0J4u!|EekbXg?R^LO(n@}2v6Tw6R zY5plqkr(-7i)5`9oaaIuD$RpJ!awDqWVk9lBD@$u%Jw9q*8{1Y6F?fZ*-hySKZp2! zz&lU|IN&F!YXYfuYlpA*Xu)|Ud~oDZ z-Iv8JN+N#zHkJh>-M#H~+bAwf)+iOuJjTJ((AL%HJ6vpwO9MY|J`F}{!Bccl3Fe0B zoyJm~oyVm5Ul1?u`{g|Why0gQy+vDo2{rhd>N!Bty*Q)}u#*hJ2*VxVlk793M+6@% zo`lJq^kP!Pp@pNx;u@F4wFA>>4w*8S0^`;>(p66b=cv}l76*xw_9*&?_>w|g`-1kRv?%O|wpP-^DJ^UQFW z2u}*a^BN{-`Rb~{m^+d9fh-2y`M!5t!?i4RJyyAO@qft{7bVLI-zKmFC%6i08^wRh z7CYdM8Zf}kqv#yjESFhW3kO>z>FdAuQ`)-zLxJ-4N~}CygD!gP`$zaLdaEL6MMFp zs)G7MI@8zaxdvs^%0AO`+`1 z2^seL!NFxO&g>A2j@JmBU;gv_OCgLpv?D*DNrX^U$2(5DG)~I99g*i)0Y^?^0|01?Qk zG?1L@E{Tfu47nIDeVKu>2`aMHi+H&}(P-nxdxaUiTsq6wW`0<7txVxfz z7LWHyzds$RCnVh$^(gBLS@9U|NXYi2;0+&-!RyEjEY6Q3Lc5jbA+2@s+cAwyj{;(? zFs92}-9z8~;A6T^xts@$$c~*57Tl~!gdc2W7=Ky)A_Zc7R`yS`fvdMBu=wS@sS1i% zZdPY4TR{oONg<5X+Gk<_(~j0SeW(3u=_8uXMIKJ+M5C{(Uu2!EZ%6n^(*MmP99bl3 z>i3da4X*WzvakLo85v82ah$?sr@5n-7wMxB9A+R^5C1ePoa%(;v+$?WeVXV<>p(7j z2-<>I3AaI)i<>%ypLJvrd&2`qm-`$m@L-VI$O)HzCW-L#jeV{3ullO|{es-V&Hiig zO7Hst1^@DZi-ZoY4XAb#@Q9T9qyNFJ5<%;%xXf!3t1uioK_C6f6v!KsKEwho|BVKhd5JVZ{g6 zN_lY&GMe%d$AXNfzMvMYqK)|9?iv2dS6X?&u!2QLbq~0)a>1e!* z+rV@mH_e^m+irpC@5j`56CXd0(+9F-Rf*^6#)?#@5X0<9=Vf)N2Uxnp;5GX>% z4CN8IyeUPRhlhcF%G{u+lZHJI%PCL4)#B`KvGLOxf2A`Q3BLHV&3xj^jlOyuJF&cH z>=X#lsH(8zDQ5JUtK1xcsh%;<%!Pu)uVe)wNnd>7q9{=XF7N-z11ySvb>AijrB)sL zy3HE|g$7&Vh9BdAuo@5IM=2+u!313yD<*buo4jgNIIR{47|Mg@kryvSkK$Ep8d-=_G)z{{l)Y#jy& zVI?jBjUZ}n!n$k4MJCTaDz*nBXRXcKU7 z8+zb@kiDj}L70muBB)Wp4yAxDuL`X2_2>RwfLkO3%(o@}yDEHY->;7FZGwMqg>R9& zUjk0^cL4jJ*W(s>8=BnTs%`*Ews?$N%cfD34DrjGMq9=5fT5Hdq0bdz$(*Xk5)jRP zg`$gcHB)e9@}JQw4XhehfZ-dURe7-3UiBEYFm$?eJl3oHQjR;;)R@$PY682VeyKf^ zzZav$=l%UlcD$&9`HvAbzUCKfF;bV{Ho&jzO$SuoE{~BN#bNGYZ#``it6Jk4oWc@0IgQ6VBiWyG ziA6-#KZg1rvmT2grop$xasRFai;;lMgp@-_^EAX7JnV7^D0ar;>T4;#xPn>Xl31Hw zhCWXmtP^JIjP7sUZyceY$UfdIX9F7fSDd^^fF_^-za+`lpa7+y9)j6-wS+y;NxH-+ zaa2o;kr2UHur%mAeRIR*JY|qO?`tc<7ij3Vt_7>_*UJ7j{(yg`RG%E+rv&*6vX@oG z+Dt+mfUx{lWEE?AkSoG0MeXkDoU{+~rSB%;E$P9IC3&c)6<>f;i+f zdw|}Ns~+agDx8lWt!?;{F)vCP)`3gK+3%yF#=&=n>Ae9lc|K^q}-ECaMRUvIYZAo$>4_qEID5h)*`|D zv#f{-|Bey*XFt*T-U6TyONa$j8&uK-FtmTU|1L+fwEOhgg4Bd=+fSb@aLBE3S^IFF z39d$-Y65rWPxj*qR>i^W<7;tP6YDzSyfdQT+Aj$8*nDGVC#FVcS|@0jEHesYt$Xi1 zLLbRpYGrCkI8iU>As|X>=enE7G&$!yF?%+bTkUUbmd|mHxm)L<>>XxU@>}2OZLd&O z_q~k+#pIrpcDP??6Qh`=TR*ZPlpE$_n6Jg)qnbR6Lgk-w38ZqP@an2oN(c9<^D<@6 zy2gO=z%YhQ(>!q5C;E1=t*%Wgb%>&n--T=f$;c*J#&PzPg#&T6cObpG{Pl)hCu}H7 zLw$RI_sdioWns_NDYI1lUq`L?XblZsy6O1bDCCCiu9YosyA?E++f{?@8*chyf3%a* zuT&-U`*xvGRZ4J2b&0d%c;fAycw+pUHbM68Ligp84K=l;Rg_FX4oVBDn(1K|q(FOt zs-C`T3R$}SHeg^wEpjcXfK`P|OCbw()tO;-T8 zE>Q1{uZMU1RNhcnG#@!d2}GcL)TkY2qgzN`Pm$7HcjnufNk!>D?pa$H+QJMKgq7pr zTaF=`i_zT;?EF}@JM_aDBRanO*Vd(zeNrZS>4M6>U_AVK+yE{8JymihW6KV{ddP5( zv%1%5OG)#?{4@<3L(0sNTb<7lUmQEQ2DqS9!Sw5+O3*H6?w~G}F)2dttqGG_7Uun7 zkaM$0?CP}6Lnv(qT6RC&F#8nQ+D(0Ex#F@{19HybMYZ2ApF>ko*XToX^b|1+)xhUC zQ{}WvE7^3(TSS_J&+_aa1R?pB_Ym$ovVfA!X6dMg&McE1YtE_TU1j{>?1kHbw|+$T zsIux|_)W_vk86?pV2Pn6YTok zTX(7-P)umI^JIHxQ?toj=Z#1!JAInunS1u8a*aKSKPjVL18!$mAJp>hN5QRf?`++9 z+#PQ1o!)JvPWh88vttepPXoJz5Y000X>faAExA)uxOU^PlOpOQ7)@d_j}W@^n4yH@Y0k@fjm|o8HF-n=S}@F7p=jt-~{~^C!@zEo;y&@ z4vO3<(<1d8QV`5$+ciSp7@OIq(KnKOi4Kz5due}hN!YiFWaWX!aD(`qeUf=aJyNZc zf*Z`*7ZRrM=-vwd3Ut1VAF(kntFII2D-y)A8*G*vTk0F4B%L+v9tG~oGftW4fr4k8 zlNGhX>@

8fwm^iZi}pt11TzmSWPvd$z# za!}<$V79vRQrntg*=$V1K_`hbp_Glcm&pN3I}(;v-5oF(RmohE^1_GUOIM*R$U*no z7_E3e#!IHmBF-y4KJZl9^45)aBVx-^L?ipb?K?ie=DJ%p%=hcbvWiOaPghoEdu3BQ z5!Vaze3mlv+uW4r?3{1BZU&zSyC+<|b@0O&i7o`xl-}N|e{0byA+>vzT@_s5O|BAe zY8ONVAzHV_wFg{p_d9j<_K{t04uvWo3b^8Or>pW!M~wTSpu1P^oILyHaBTjewa;Ac zp1uF(Nb>bV>)u_xd-21YqeA5`pDh5N|75v$glnwvvthkGmKfR*sjU5lmQ#m)7Zacm zF61`|?>mQ&ROYnyITwK?9xV8s8m}o06SZX-+c?!5C8+CdHQT){kvS+EjO@&~3SjUrrb!mI;Ty%%#hVZC+=|upT2Mw(;JVq|r!zZn}_K)nk z%+wMO^pJ~eq{W#oFK+7FP|&$_Ek5CD)g+qMTX|`her{(`iF55hL3zTi&MD`blkjTU zw9n61Ygl2nBj;wDuI$^?qORz856R5mQqyyo+WNE0o))l^-*8bQM&`I+gMBqINK^Ho z*v+{2zw9-r@?f(ux3aH^i(`}$1=9H&F4x;;0B_k41w-&s0t&-Ke0#A1u7g}+2{I8_1kJe2ZYsAwPBje~#(;l5ZU+ZdEqg6~s zp1Fy=KQhvJE$&YnbhlI> zw}<@s9LfmIab6GbYd1(Lyb7ZIJTWC;Mww4h)7; zlSen$l|1)fdSUNjH$EV@YeRdhT!mBULFeOc7Nc%!O!#*W!Zy#cxlwMCR26QF-2lF{ z(_R;8jrm#b^%fJ-)jifR0^DE0MimQ8KCj-=sf0Z|uv2-f*G~_X6cc^YCdQS+3T;na zKK{r;IwQq-qhf3wF@okO! zRyPMadDW;@+CswB;i^TMb~eqOC28M2j~(3QGB zXVdD8uCnI2n34HL}Zt6a7M@F6bC%#dWmKK9Yc) zZQ>T@@`p%DO}>+!cvvyyeS4b5A@F(ALRy|&SR4Cv<#4+CFAT$TjXceq71?x)&T#Dk zt?XCk5|zl#EZr=G4F*uKs3((|t;|w~x@CLA6!jzq|ZW2j0BdHT!t4K{{T3biS}N8$GjLKY~t z(AE9Cu6=S8Lb|}dRB4RFJh6iK@kj@e@iZr{ zW0|8@uMtdk?YOTU&?j?c-!0p7`n4i;BW*ZzC-{}L@&jbq3`E~@gQMly!q%sq2!)}{ zI>+ts)hD=hnC)E#ZSq8s77bP!ph7w~sEMBy;Z@=zvKd#1+ zK{AX~PWo=!)#;=aX1Xz?L#0_Zz79Cy7qe-Xx1@=|U2RKM&13g^|LFh2f_#^?vqd3X zu6Q~`Vf09fm(d=`s`iT%$M}f-iJ_+-8*kW2H>?x)=Bk{+-%xnv6K^v1Kt1#Q`ZBF} z%sy6tI$beJx*WZ8ciT@(>uhcd?mU&gnqGr%KU+iTf zo1u*Cm8T>mZ7(|N^gwl=yYaC%Q)|NxLiY_7&ckXD@v9Y<6`AgTII7vd5T1hi(euec z8#gLFP+~mX?=czI?t$MTj~rRJtcr$qItf?KUN@T8#-}aM0y%!#D;Z7kcf@xd;JV(^e2==Y-J{Mixcs zwwK_xbR`(|M!Ld*8so(5YrW) z^re1)KU*VvBqrZ%Z}M}3Oz2)13h+Z27Cf|K@3QpM$qRoe=)CXc6;L?OoHqKOrapZK zNJc}C3N~OKtuMHYhI_-F@7SSZzV;aNHcF-ii2&j%aq26hRm@K8_p9{2$e87R-1)dk*QB>e1%mzmk;pqc23>U7dqwix_6>c2qSZo?f|n`6t~l$tBovE@IkkQF@q%+SY{Dty zuvnz#a2n2R#cLdfHbZTgMEZA1$vuEYXgGgds(6cNekO2y){t9VVjE~2H3d(QK-C%< z>lkSJKj18=&THDfOoaCLG0f-f^;6r2iRi1P|TUYd1;IQ=LX;lYkc)8XxAO zUhp_dxFs-Q z+cP_zRt|g^0|w1m7ajgr5K&<_LHsb&FW zi0|-alEEYK9{Q4X5#{g&%nrn>?TBMid;9VRy3)&IImhls@lWWrw&q-|h=tdg`@c%M zD~9#Q8+)*&HE5_-nBGbG)fN%UT>S#pXW|pjSb0N$U=74T_x1tGnzQe;eM&7JYv|Fl z$bEglgx&5J@NHL5R>s>q_b@bPc&sX#&E8iB&+QO<4{!WsTHWhTStvw)X%0SO>-sGCjAd%0?3iB0>+w*IRcP)6=zAd4` z>r)6Wq~ERp!QT7E-fcg>6Fs}ks>jPh&Mc%+iWr?o`1;Sud%sr%<#}9aVN3ae7&3RjBFO4b@}sn)i(ATq!c{4GD$_U=t><965*i4Xe24B_az@<7_iP8;*qU~ z%u5k{K^=j@ZQ6DkiiRCS_d9NU=or>IJZf`zEa>o^U5D>g9=?C}@WcCufBJBELhHzs z&5_4JN1meW5+z4oxF|f^c=&5&_&Rh^$|eLkI_2+Dw*PQ+|KaHV!_obRqx-*xqs#d} z$k9c=5FY#>D798@=PQe0<^0}Id-sjrxhTy>!#8dGk~aKr*oe(|(q~H8@T$3j zqI6($hSNb~J4`a{ROw-g;h#@FVBGGAODGG@%Spzg>PYuh`LX%{rWT4UTz($Y zrBqk*>0@?C)!V+JhK@7$_IvXq2jgX}4DoIdv+%>b@ZPokL!|RUSei%^C|<~F02*fe5oNLfZy7FuXaO|XPCE?e(eu0ksx-DJ z{V=SbI@|&@p`B61ebI;`AE_vlTIKW#W9l`VO@smplt$6)<(F#4d zh!$uul0~Kqc1=@KKWv)ZS%r5LUq%__JQ~tgori=XuAQ!Cdkj}SDyaKS>8I-)#YWh5 z^y|$`-a55N;H+x>JFvEN2;!iNTg7Em+EiEQtv`@U=0ZEN4l=O~^S<@S@UNB!IbZYi$6MOf<093AFe^99R1WJ3zy?#i(di`YNkkwuTmESPncQ!x zMBB1^m`6LfTqd;#k&1X%+SSi?r zx5PvVY-1I_`ENf^uFhoW|>|tz{bsn0^SW zflss=!F#yo!z!NaGp*)Uhw>kgsWdwUA!nD^bwH%}^)Gxi7{J!Yw`9oWle-px!b!F_ z`^Ws%CNRFCIjdw?ys$6^&-i5^`6M(f*d|d3ndnA*@1IdeuOj zh~7%xM@&4iWQ!?c&7ha^Zagat@*6zu+%=5DwE8(Pp@g8XykF8~u`wg7G;ZO1Q}13DIs=FdSRLd+ zR|VF^&X1JB z%r?57*4o^VbL~Ky;juF~I4&=BlZ=}vaq+mr5!e{Qi-onvqP<5XbS+L7aji(iT@9u>N5w%gYSYnWx3 zal!<3re(641#}P^3F*YDZNWpX8TCbaC1FdlLfYWwhzxc(WqR3&OQo=Ve8aX=OB+Vs z#o$)K gBOx5^#RS}|*6Cj>%dXc_5Yqfam{3YT~fc5p_wCh+3+MFkq(2HAt{^wi^R1KF)82L*FHDz5cn6!RS^>6bcX(LYT-r0X z-rC^a59ffV!czogD*&{jUHigbrzH3~0F~lVJ+~J4#ENaekpIVPJ z_`946%Ot}*uy4vluA>H9L1y~#t>P5oVqR#%S*c6GpDw^M+ZS}ija%x4$p9@o>k4gGlHHEIJ!H$36*kYlZ9`eta zGO!fu@w{Mg&(YTdfRFXI3&^@v)SQKwiBdIh?9a-(>~#Lj=0TD#hG{=3UN+aGc3|6| z+81j@7FknWvL)w8Iu~=&lmA-yB|qTXFXh37T~x`7oFy*5w*0u7$gbGK_2yXf7@HJMbg` z$HS_`h> zmJSkXK>_KZOjsb&ju(KpMy9P8z()sQN@5X8#|T0{?GoY6GIY#99FQ=BEap?CerTh) zR3kBYXP8rrh2H0c6UgQ}v4J46utb-!)u|IeSv>nghQ``%^^0%dYX!-EWX-e9yT~L& zK8}Q#f|GcFKYh2Wm*uobr(r!*T}W0=V&}yp145zev`mL)-R%aA*~eqIi1WES5FDDk&O56RnWA=9IxoNv#i%Z(7wiPDE% zJ{z6T6ZcFTYDvG0q>aSG!ka3{e71|1e5-kA)&GFeqpq0&WIomgdc#AEe_+$y9t8`q-!ay9x z8`b3up{){DO#nAcxDAf6I3=HJkZP`Q%))iVf--YZH`O$nrj*an*x`%N6H@7p!(B+I-YfVTa*lfGNJkpy0;S;TST3d4eH=^91;usX4h$4td4 z8?ZYbxt^+duq@6S-2gAl-i1Ysz6PXn@<|Y=AQo_q1Dh)~Rdp`QWGV#Ar!TwJ;CYpt z7-98`13K3q!JOOTWT>&}MKzhRTYSk?kGagzCFk4|R;t)$WJjT{quN0*($??;vyxW7 zx374SWc94ODynWTtPU}n)usTowUY(Q#jUHE3AyYL6wCzQzqRSyH`9-gc|b#7i(Xxr z!}q#OUm?`4>ha55z6w?dl|WEKm&L{GRU-lDYyN-(cct~z8iW}eLBRzM=Y^nGT)W$z zT79r$!cLoZ}4f|HyC`W&#;TB!EgF>!B<6C>i{1K>TVf%m6`7I$>+s& z+?o!Z@}x(1(rLs!U_v*MUaq zK$inw45nS*U@?^(&kS{3JNY{F#OA&#wcn#E&b7|mL;?#Mg2R%NQAo)VvBH^RZ*}LY zb|eLwfeY@SbuOVyKF75fzY>}G2e-PL0kq8F;X!r+KXHWi0w8~a^RhAIn;&=Zsqd97 zaQkXL_TRxvW6d*}A+e+(pejb%OL~jRDiIyj5lhHbdEqsV#GHT$-d)YeN+;=BuR)p* zvRGD_5H|(Qt(o`FM#uupGbvKG85)qS^ty(}?|Nt!O0R`fy=vwlEf9I->DqOu zgqD2})T5sYox_{b_jnImJ2%P(rk`dlkVVI)nkv>`8Py2Dp58#M4*D>tUeih(%9S&) zcu{2>9$BN{p@R(0g;|U4u*>XZFZL#91OZ2+&+j*vl_FP2XjHxoQ7{Ljh8;6 z75fI2h}d?>EWa|JwI95C;)Rbq;C%{^dv=4Y_=wxDq3DCSakq#zJm#y|n6>a=zzodwlN= z60;ZP6>jk$zY1 zO=cH#*BrL_kp-`{2umlxy!<41E)DKDP!&Pl6yVmj_XAB&r~b*0e_egKVf0e*1mqVq z#1mHw%XW~_2U5-Yx6-R;xDt4J95VW~%Z*rlTrH>#DDSctb?GCisP)otvuC5E$dXl$iNt6WPcytiv z&!ERyMpZ(?$VLw8a+cU!g`2g*^?uA|U$ZAV;l{0JTsk{BwT^_8K<0n8?V2n;nlGYP)bOBJjP8rb9BKz8Nj6I?X|yYLL9wEV6(t#Bt9O z8VJGL2euB9k=+@uu8SKX*6^@GNOXdB!c(7BI>yK-!&!&@I)x?+7SL7M^d;Cd5w4UF z!p6a%t^!f0nh}%%r)L|Yx2BplHBG?l`J&p15LiXk9f}#&4!G37AfY{8WZ{^Rb`8%S zPV>yg2Sg90L79VIGs({}4Tf)W8Oa+xTxZ;p)$t9s-Q&Cq28Udvp$heBK3v{RI>F1A z7%FKA+LZ&tv-J^0%w}BXF4#Knu>(PB86N#UW>pUH#Pq8ioYrL4Z-hReCKhs|J)!ua z#V_#e3IeXsx@N$Xax8aChnm&B-iIlq$f{OXz}7CXbgdD|qY{#VB7f)zMT_hb~b z*LZ9rWfRQ9=V98V*j8e2Y#K}>;K!Vm5QN2e1!6nVG_^B;xfeG6WY?WBk!{l^i<7fk z6vJ;?omEb8|7n2VPfh2YPu{uNObd0<9=7g`WH8$C$-?`T<-ww)UjelQh$QZ53vE8} zdT0^_bM%)M1E#}exk4C4WyO+ZO9=R}OPd>2%oG#@da;8Qas*p;x+{N!N~Tblus3J@ ztcT3iho@hC;;GN-{n(Z>%DQl#6ONarl)!TJz&TE0YJ`I%-{EPyG!FAU_4*A)EZa<1 zf~amTm(OGz-}Tf;LT8J+Jm{MM&UoN*r@k?vpXH$l_ZJs*#+_+5RP&u@gw&^%j1*u0 zJ!za`lk*v<6|0aMrqCQ}yEeH1y=hJI&g;%8X4IYJ5gO617}GO=lhEVs9{$*_yRRL) zVqh7PA+En0N~ab`=Iq% z0{pN!`YDi}Mj3O)9sK#4!CK~7r9U?q3V*)t=5KcH=B8@xFqeA8v&(tZfP+CzzEi(w z&8nR@71Si@$#2_f5b5hJGxjW<0G4Bs!-p?O>NB4kR!yuBbDi`Hru8r@{5_aYj{b zi*(z^r;7H7p&ffYo88cOW;*It(E>-ZaP-gLcK`Lqu9j8vS3LKYD+x&6JyycOeG7Bv z{`zxf{|_IZdH(g`!CwpWH-A`ocXQv}O>f^aM)xVs>RxgZo_e>>OeBPz7QWj7OP&ig zJBGI&*;_!u;LC9SOjMz_`uMXHwN^DVe~dLAv*g|`>^z3dL(>L?#sT~2P-{w0VE;3u zx%uj)&1mQV&QYfA!3RFxN}=8}Tr0p_Kw#{Udl~n{E?N6t*~dVq{1EX(uBc#`-C2NhlFnuVYvQlh&Iigf6KTtIeFukS|1pFaeWN zg#9r_uvjY)0#xx0&&8`k~_P5tp@M(y%NGK-W~k;N;44K5oB8Z2on= zV3@TZK5KKV!6#|Pg*FUiiZX33<==R287Cj8 z->gK(22MHH-b-9qci|PJ-_g1>)mnQ) zBJGKQjUc_=v0-@mX??GJ2_hxHPRL!O%e4=$MUp8c7%n@XyFY|2H%dY1@&p2|6K$sW zT~sP2&BI=RmS67>ztcJGwEfO(&Y-29$E~u|)TqQ{KQ{&z)w=IRxc^lcTs*V7xD(-n zu5k9Hp25h!9P>CFsPA=UPx3sZAQt8|qouZfNw?#gvlQc#c3$1;0mZit9L+v}f>y7+ z?TWXoW-L7SA7^-uIiMfB_aMh1?qs8{XZud}ZpW37wY=P0r@?onngf9qBv!EGj8{*| z)`MqiI$Au*mv!NBflSTf0}klFve9UJ06&aWF082bPJ)|Z^u06`a#;VG>4>Q z$xu4tSip!3a%DsiQde5~HN8G>> ziOp86fl>F1H$Q7UautW>CznV}Uv_x_w{$-)M=vy!_t2K(>kaHTBwH0VqhH5fTOU-1 zl~?yq)_m3s_u4Ods}&!Oo4&lWU}C3e2ct)Z@4eqaDn)P4%v#;w5itpMF4OsO7s1+} z^rSLzulKI&S{f1l;`Nz_bNaTgKZV$K+WpVwTs;e;-}QCg#Ux4Izx|P*Bnznmv)DyOhCY1t)E}xe*MbF*S1v|zN|eQGrl)Ygf@&TdfnLoz74j|(HWod0g6aRQ#326 zL-q;=7#lOza5h=WN_g?m+T7q~hM|*u5-WV{l7ZDe!{Dp0A7z6VI`DYE?0P2H7sKs^ z#WS=Yt%!9S`f3_%!HkA)4Xl{qHhI>teCLRH@NpLpcxmidcCmWc)=9s!T>m<jm{AYDww6gVa)lm5x0nr3k$bMzZ9C51X+1wExB4n?@y}|82jD zpn#?bsAOsmAZThhhC>C21LgpViervtYUMno1qdpphA7VSl%|>5WV4c$mbKL^ZFb76 zy(_iJCig?_|9!9f{-0+(>zuRJd3A1m>)YbX@47yp%MaGx%reYeW#M532|~05tc8Ox zNtX*n&P%>sDdvJBlzlYje5bIAMhn0)z_Twj`c_p)WrXjZ3-gut_ovWR$k4<*6|tSE zi_cTH)u6Pe)?gk_%nYSs6dk1b8NZP+*jSt=Le?_Af#^vfFOhFtS*r-HE5HjH^UU%W zkGs`M@zJ}7N;#O4ym3hF!rRxt)OAt2R4Z{KR~T*&)3^x<(qriq>tQ2xlriOtw;}!t zZ0j*Jd<1iY=s(c9?-blJ0p32nrxOOOZ*O>ee&)^6K>tD&Ux*ZNJKtUW%dkZ`?(NMj z?=OFR81w$h+M;c(hvr=?!L^-kmYM~&hgZ}t0dt4m+e>X3o)lN!{n#=1sLvOXqzief zj9Iq1j2!xw@$p8^bV~nyh%giWmg;-idjyeTy~@X)_Y~d;@RqD7^wdz4KVVDpsJuJn z{&QF?y{XG=7*MR8hz$f&jD!Dn4;{heHiV22Ll%ZB!sVzdWoX&LI53WJ>5!NNdJ8&J z!SH)aEe-x50$>fm;a2O9@qY&LF!dBaUrf27#YgN`CG=-(#3mdHTV?inBpfGYNSn%d zA)4A#o0TF&8WNVd$>PlM8#1a)Z>pZwU&IWI*+f`xCtadm#G|CxeQ89Gb;60xXei(mcEg zvN!{AXFA%(%yYfP0v>$nma?_|?OriKeI}MJ`5rtBcAbP^592867gPoeeQn_03!Wv; z#rX9oJq-gOsEEi$clUs{;NrZjKU5H1UV`5l^*NR{aP=&|77yp;71@{0<+mgmAZbm4 zdWBZpsj+4w5Fe;l;jF1-cF`z4)*|Ng43dMxU>6cdC+<`S_aj^08@H6BrFa+CL8q2# z2Uxip&PePBKA6A_@QWhrX^>>(qiEXv$&HsCWu?u_40OttrpuEI3Op9}SIe!hOvIZ+ zy2+f2s#2V3z)v$cs^d2x%?-{7i%(qlc0>iEc+%2iF7JNfJsD#cd9vvBsk;NQ(4k#< z$D^?@rx0nn#j>h*skOkEK+ZAO7)BVz1OB2j zL;mHEaQOGn303Kh0FD0^MadZd%i6H}x3wV*gH$p1;fCr#{U8O=;wvZu;>*GUYOh>AuJ=jk74uW6hL@>;BE z{<2F&ZnFl_CBI}wv=wwZ)6JUWb%(8$Z=?J;@xW+lux1_Wmr$LprNPcHz{{X%B_Ty= z7WsG**g?+mFw-X_vbp4fXSX5#)OGzNP!R|3(_A8~GFRFjGsb5K7|HHzA}4>bvZzKu z!?dbNjej2EN%}L+0jTNKTGbN%!dabe# zfdorg%KQcvFXGfm4p-vdP+quS4m5CxdoZYZ<{5F+GRdFRVkgB

j5#!m?7CMtGwO zR*9sI8e%GjhK{&_uGQd>F*0`td3Y}-+{F_>C5>Q(&X$}U(NYQx<;z$af}n*1ljvbT zs~hMV4P94?xMl$2sK@GUEEF@$cxVS*iUTI=_04&#nRB-s$Hvd+xUi3ZszYfyk>k|#VXvwEk2(PcUrZTKCBg%w1W6=ob# z76+Lu7kI9g6YFcFBs4t#8!@(i?F$QQ!Ia8BvaU$8gw+FnXI7cI4@6m6{sxltib7T0 z?+JfsA zOG}!dGX5mhvWF^Vhc{SkCDkaT^K}LHX8D+>bKg5oKIm&%$PmZQ29oafF>f3dmNvAs zm1kOmu=POeIbsO#$wCRG`1XjJ0p6%UZ#SV~AwWbS*Ga;wu;>Wq4IjC8VGU#OJ0^)? zVvj>V2ur7Yi_zeAJaF4+}GPpT<(h$3SPY+d6zB^AZV$d@nlcJ*iekf zVve1ruuGEa#rSHOxT-{F*ON|QFF)n!yF7}ie@HOBIzw{vV6ceq z{XO}nrb|a&9;(5^UrE(h-qp57INm+*5Z+}BJKSeTLOCY1LF1tc$4*dQ$WDOx7NKw; zBs4wNTmUsSQdsvPT+S`u2&~gy+@xmE6jCmy?WDJ94GcNqys{q(=h>m?Itn(jz0&>DC}KW zfW%sNzb{k<)x@z>9g->8%Ug4e9*cbrfDIl%&xG1J4Gfmy!5K<4H{w2K)j5_%vt||3!?27OpoEN>YwKm z^ecS`)!aC^S&mVGHuiy9kZ1W=gd4!+fh2cxR6aGFl~mR2+8DYwO$G0bPryYekfS7Y+He`Dsuy=<+bF9Ox8el3BQBoVdZ{+j} z*dwW?9d6(P4lwO|m5tmS@ac04m&Yf`hw9^kX|3M+hHFDSh0Ff_j{O%{Zqk;P{B0sr zVAqPFz|?@w(I+O+)jO8eLILwHB~jvekP+0C6(Dm>)8J2ZB=Tha-|N2v)-arP2U2qn7Qk{)vo#C0M7slQV)J?USR0>3C^7 zK$dd|H@n?}({51Pn=E$5Q|A)&v{UM~+I42S%^s42K$|)q0Z)Fbf+`WreYEK|Tc$>i z*2yN}^^|wxMs-{;X`Ev2;Jl*;<+1yiVXKYXUUuFEV1-mn}}~?A3j6dfwh!P)%(uXTMxOpG4)$A-0zt{9V@H1(VDtoZ|gj} zUfV?G` z!I!O7g~DV?x|AaPiNy(88&kq;g;@^t_@rUf&;kT-E7yVL% z`@HWUj7MO%G)!Mz*-n47VsjBo2$jZOuCDNCzfPPya4;8Nw@;vvH#N_!tUu3X77=Y7 z+d%xSNZz)?+lSwGte-VNzA=p)@#1feyGtF_6Qtpwlqfe%4@bx&j;EX$qQ} zWvMb@+!VHYKU-rORzA+wMsZ&4J))!o((p>rMX`vaUjdWJ8V8TcHX~$PFte@FvI!O0 zHvQRlODyDNIMISRbTb;gk_1lm*8OnCwA(7`JX~)cAnV8Zm2%mL_4do@S#Lvo zhv|w1fWd75PwoK`5kxUNxNikiB9-hnfKb*m@{{Z?b8uI<_}K~g!bBe33|+T;KrS@n zA)p|cXch}Vo@(JQIpU1V0U945<#CXWcoT~-yBV(3Y{X6_y<&&g0;O9H5{5WYN?LzLm1Iqd z`XHDJH}N33g(9Sr##@g9MMU=N-3YHhd#abodQ_{n}T71@Oe*yDZg^knfBUru6H|Z84+iS<6=FPOqE*-}=J^lm0f4Ba)`K`A#A%|ITHNU4 zfbqLQpHLyczF_u7oO0i~S$hfWJrmL60PA~s1aV8(DW_RMx~!BaUC2Tr^kM9&Jv-Tf zggtO_3Jf$?N%bT|a%AwwLpN*wpW%Z!>z7TPg{^Akdj1Eu~8h-_^H5M z%KFYJA)6#o)RBKhQKkP5wpmDj`hVt&qE2Z42iV5f%XW7>eEApHcGM~noYVV z&sTt&6{vxobzrsAL!o0^h~@AhgR_PtL(A5~Z}y3FD@okg`x-2Bt1y&R->X{V4L_IHb+a*$A%QQ1>#h%--yDM!H-*u zuf~Or?n}(EuK_1&{W)SJcJa|?o7slBzY!N;vmTozL63VJ*?MG-yE63tR=KX+)hks- zCy-$yes1u*j5O$nkcT2^VpFt0owp^h9OE;vl7Q6w9-9TwT2Sj z(A<3U6JsRNFte!?Z$Sk*B4tDae0))?Gzn2(fOW+P3buKom=@@kEJ#DUP`ky-8* zW}FHDb(LH0>6$!@?H9rFd7(!71(*>1k1vU67Q7N)?DJK^xh)XJYQ&@QU|*S^+}F73 z=dUpp>*yXs7|NT`G1*`fmJUOO3TFOfi@<)ZN4zTx6fl@}KCJTK8>g|F--ugXWc+OI zM@UOF*?`C)7Y5K8#kh4$##=@`VfB|Uiu~IbH4=yb+0y>yi?YLwl{U+&$tD6w5?|E) z-@YgZTgchpzNn@*`GV9?i1kuMWSyTx(WgGyMsgeS8>>NQ&J7%%zDj7Jyo(&C=oK~K zalSbj1`$#41WzWUF4Lce`T1bi)(}GkgToGOa(P31tvQUH*rJs6al%`kYuPRpkSUag+x6QQCH(D+%FBQqVE^((aS^=vN85FT zw%5-6_C*m3CBCSYq9gz5i;`0#oQ`!Ua$85fo zB0!ctynO)6Xf3RzsW9~)M$e{Z46;p;hL?j&;@KlvpWyUAb!$to2QBZ`nGD(I%B^psC&xM8WkBvGMy z@ct2wV8E@yCAGvzB8l>u4D*ee+3p&;MYc#Dvd3Q&i{UqipZt2bvU-IXk`>h%`fz0$+wPQFD2s9O`dLl)9Hl#JBjDBx=toc1uY4?mWF$ zvbqcoCB}c*hK*5d1h~Y8La+PRwixxEHff0oTD$Fw>5@E=j+VAjX6J}5h6N{}r468? zF1NKVjB^&6JHh!Y~@f7{&-Z9U=kDKV1Egzp41Dxyyda~mo(8%RT|41 zrxgqYu-ALzwP8{XXGUMz$((o}65p~poi|?Y{%~8C^0z?HV0GoSsPh+V%r;BCwIv2z zEWTt#8k9()igU177oZTh&2o&4dg~A!3gDW!%6Neiyo_wFODccr<_(< z_X$HD8IeNUDet_An>YeQPp|Ws;SxzyDE_Q{$d8(*+WI$((^5$iNz^!HeSLB3xI_{K z4X^P~I1b17Jg&GCdwVpqy(0`E?-$K~_37N!GFBtyt#&NHheU)XCQ?vZaV^+KU?&^h z2O7hB>N%<^j%IHrcti8LyoaZOZ8@w`cY*-l^UZ#a5@hAIVd+&%!4}#*p@!WITghQ5 zZoasVsO$D;u^oHIJXg6RG6;ic>1mFB`fp!oS-7bRWagV z1$+I_fbs6**snGpf3BtueGcAwSK*<_+p`+{tz&-LYg_O-ZxfuiW)&Mg3gv2 z+E;9*D095z;2Zn({ApG}va-oD(!OyJhrNb+L{LowGK3iBE8zK>a#byGNV?fmI>a|b z?I=C!SbSDAc%$7HP;m(4`As^`nfdrEj64Bc!D{>8@pj=|!VH^}9{c!~t;tZ4ciI0VtAXW4=B*UyyiLCQ#y?q9u zQbO5rnzI2XESpW?#7VqR@` z*W5lb1>{vCQ(QuxFRyWqtnZOg#Y08?{ONTA1tomVWe7szf`ZLJ%Hft)aPF5H%2N$ASSlPm8Ve^NAaSE>a{xi2AArnNI zVo*dm=mG;RBew|moeq(zHO2sMz-Ab)!yX}+=oK-EDE*>`5dg+T9b*>)qg~Z%~ z+g8DN4$QFYtmRJ~*1a8u+}0q{2{}5TpQ@!LD>3?>urg)oP6qd=Bfq^mv9T0%@nWYR z-u6`oDP_FA-=<6Ai@KT7HC)wo`{Mt}7o|>;llY>NW=@X&%RcgdLDS--A4vcAJWw)s z_FvJoxl)h+f~HCGz+Y&Z=s(f4aIG%Rc%(gB*V-EJNEz)SG|N8xgF$YmbKRXaK#zD$ zba@J|C_`$bv8PP-6xSf_rM#lyZ@yn@jw* zgC3!2JLz+~V6o#BinkSAU6zf8&jHWwi0vzx!GVjqOjJ&!oqqKP>{Xn#&0NX&oytcS zHKs+KQ{@bGm4Yj2Cr7d8wv4A$f)0qO0or3yBGPoDwO)H%zP7@sj*=wvi+YZ`kiA$uoUsaM=u1H&2i|fmO(&=(Ee?Q1L6+F~noJ!CMng#BbH#Qc zBhiY)#cAUAEHi2`!Y8c}&tUYwBzlzYjo^6?Z3`>b4Wo>g(Aq3&@&3_Pw{z>pLIxXx z4wJKi?aLz6hKOfQWxGN%BKRWay*0i3jYRluv@Li6jBRFc9w2EhgvFrb*a{eC-RZD! zL8zBLGsF%7u7x5nemNVfWM7W(y}WIN2Gs{1-x$dS#5YB%3gQ5pLq3e)Mj<7cLI=s1 zw~i~M-c>@N!R!p`kr4?T8Y9JgXIwVl<)23{?Cte* zaL_fqlUh3};O%g%wfYbSiKnJy^d~n~ju)TDWQA$oJR_oz##9^M z<45LYEXkn9<&dPGB%jex5*BOseh{bP5$63Fm?fQqO_BCLoH#wzJ(eQ#$o&So_?pYw zDcvDN&a9PM-BiyF!$Lv>Nu$wfL=(16np;oQ4^>;i3^_FIQpa?Wk;^s(>mZ=n*(>o+i2zkGyP_*HL@JQv z$h>^LrZDmO2@0fU#`qq_Sl0lZhgp3EhM2^pE5_Buwzm(!EB^ABdh#skTyyC)BY9A3 z6St#J0B~*yfj0!u<*(s|-)6=ck^t^Vcb__vrW<|~2}VGi1@>=>5xxXxsBSTb8W<|? zn^z6;_PXXlXgXTvx}dA$Wp<12$v7L1akA^{lD&6xTLIEJ8@}4I6Vg%+9Bg>2@d~`A zZCW9Ojp6LbT4iIaz*Ah*MQ41jJCkECq=JD)bY7})=Rsi&fFNxJQu*uP!E2Y@Puum~ z34{*SG*8x(Duyqt&cwsp);-bW$DVgoYy?Ne4HQU~)Rpn97GPoz5DSjr>#}u9E_>=j zC9M;8j!DT6#-I+i(FNJQOIw`yst@`~iX*_t1CxPi3Ugc!0;@&S-wkLI&$);1Rjy2G z+NB1N{<{SUO0v^2kZUq6o|J0?6~81&P4};<#I-8TzRK;hh8ZUH`T-5Y$&W`GoUM90 z$e%)_cCIuYUcFs;*M>wpR&~Rl&52S=5cX$>v&Y!d0 zIxBMDe5pTg(7NxW=9|AuEBfR@&V_CG>Fy5#fT(sXZBGF(@D%4X66LdVHYgU z`>zXz;WY??B*QC{3cMdLHrwxe;nrcdt7m^Makb-vvHCD0&pe`=y=AUV8qxYy@;%-u z8Cg7$Xr0)yHK2bF!H1k0rn03b?A4*&PEk4+EC>Z+Bsm4UfIqa8X(>8jm?>%sIU z|IoA$#+yJh>}u<9u@pT@V*+5pYkZv@yX$JC@CEVt4~R1oBfsrC8I$?_bte650AF>u z{BFy}(aGqF@7mHIq(HTX0Bqn~+yS$7EO(Iz_VDCCl#<~b= ziE!Z>XLiDVnX~D~4D9uBD-{!kK7qn4Oex8|T) zo&bZxcNTO#(n&4{V6YQEUQ&66qkG*yR2`&rSpD(!Ze(|2-u&fPM5T*kx$YmEHvL?3 z_P97vE}1C^sHsz(s<^K=QF%=1#~dw}`tbO@t36ln9Li}oC|;9{OrT?Pgn1t3mMB&kj(Z3DpEN{sWOjNw4eed9a~I2k8$o^ z6AXW;fhvByw0JD6Pgag*|8P~g8#(D6YKJ2)@jg^J#(`<*G40R>&PYuInpq8Tdrw`^ zFmo(cede^I+i;AHoPE93RToe=CU)0CqX50H&T{u7f>yu0_lO-tIOvf6v|m4{tF77c z?TO*-vym<6f{NRZAI!mcb3a;egQ~O!EbY@>x9#2i{9Q)uvNI_uM?wW!J#BJ2Rczyj zYAO>>c5ku!@5g6Ukz%!^w=|QP)!Wz`d@ltYwr!4cWbjl=4Sl%w%2=1=H* zL4$EdB=RRG%$Gs_UQph9G`h$q;t|2S5=(Umqd0%mXG|0pFQwiTDW_2&w12{rsHevj<!Y(t_&$K9S#FXC%$3JW@uvJNfN}FIn(q4I@c$Lj>kUMri zDmb*y_BbdPL?>t!D}zaqDO8O5xSi!$ytGV6jsu{iGCtcnuq#T<(nl2ZYMX0?btXH= z^rq@n49tI#PooCoCvBuW^pq>~j}Nfr-^GL3MZ^z@vQ`1=hRo&(@se z`#@U&Jaf#*>#9L-nDhs(B)*FIvfIY1UQDZ7>)7aa+N^Los%0b@rX3|BGJllq?DP{LvA0E=3a=z3&`0+RP>xqJBIfIxZE&PxOyV&N!ZN z@%VP9>bAX`GQVv+>;h6hsMem7(XNUG)r>)k#+vgkZp?Std(5WeUxC)YEtW}36-ob( zP4|Bmul{H8>VFom{(rl8#ryw|t@^*Wctvmf*MHA4O8hhe!@|R)66uak3k?Dca6rHedCzB)qqRfp%L?4sBEkB6b)0u za6~1!#8AaL_s&EmnWh*v@U5AmaVFZl+Ise(ewj-*%9n4rxUIeTBdFfv*n)T!L1IJ20W{3QnVq zHOZaWQ)08gzL@cRu?x19!Ev1%dgl?f9k*x^9X@-V|8fE}3MuG;*E<5s(6z04rb>00 zaFYVcI7bn(zJpB}8&?Bk30Le;dyayX>hCtc8uDafoG09vOTSj5Md}+CfLJV%#Yqkw z_1#U%WtjeB(^Z98^gc_P=+GWjW45ThNPqpn7m_TFTGNoEjtmEgA`|xv0kny0ATlyT zX=&@qOPg7%tzSHY0M@z=A@m{3{3LM&T9CT!oaJ*OIupPqhPu+}sQd`2mpCt|7p54< zpG9tngK(Sig*1{NHR$j@no1&(*nmie@fOf$ya6E6ZVfSH|MU1)doT_YE#XzN4i6TH z0kJx+2&BU!49ces0}EqkeSVq>x515q>y2#2N&t;`C`RO5I!!`z2O-#XW@Wx1rl!ju zvH{o{!Y}WvN*AKfKuX0pQG@@VBm)YEgZ7P$=Rs<0<~mwsvR3$d_aNbK&>cQuoo!m0 zyoWdis<4UZI}O(AZuRV&Re4;Of8xA&Kz-p%)nkJ(G|Lnc9N;E6zkpRrYpIo?+k|ZF zR|-r3ur}<=$CcLOGL=Df5x*M+GlRH4SGEW+GlysEu0Xchd6w2U=ICDrJ;0s7E5g?J zIdoN57`6ztHj*(}MGZyNXj3OgpLJ{-lq3O?+>@O*1HI4FEBRp?U0`nZ zHpxN20Fd-NT?k1@m8+E%N8!=q78{sq6o5d5YB)NBSa`P~C5ujkv!@FD&n7)^>tT$j zp7K#mO0A(M2(NuNDoULu7t?B4Yw&5;wU&{Z3Kkf(r+rRxT>A(L^ zW2A&0m*XA=xack}x!I}kHwU?*4Ro*3R$gHCweBjccZvP0U$}TD+w|boh_B=1ArCyT|ev`>7Cfo3@L9B`coy0mrLt(0AB=6Z3kzIF`>+2Lh3cRKfPP7*f zlMEitLY(U*lXL{JGR|cqFR~r+X3Go5dIdzpmXeSsd^@N5j}v8iv4Tor2#rj zBiW(7^2B|i@6`dLSr$SCjBaPvos`U+w(B{PFrQeR10#Sx;YM$N$=7bmr4;S*G}}|y zq3J#`*Fg0T4N*^T5JNaU_KA+`amTc~=z3sZ!gkc*%OR1u`L*}2x_{S+3mvjPqzZn# z?Rwv*bo5h-X|a?)Nsm=L6@2YKU2Dn^bIF5tf#gBEG*frP3zE>JMi$k~Fd-PUCErct zN`i33)B3K^+KAEytezEtkImHvQnAf>)5;LTzBHX<3WcjIJ&PYH4w!mZQ@zr3Z!w8xyQR~EJ=*6kBEO(lb-wl`wzUb zPIDP)Rn>KoZm&Pk#e0mj)Q7hp25!`_5c*YY`8VML1T`Kt{pXXl8`#Ze?^^g5mQwv$Nkza zjO0rm>bEVfb*h@ye!Kzs6S5$A0H#}#;C!6JnZYtX(w1gjVrxdmj$ z8#UE{Wqwb!j+6!NF;ehJh%E+qAa;fv2uB@EHz3Qx7r=KP&IUrz0bx7=@0c-Gj>Oz;)q}MNN}gD44&*_VKCV~a(`SZ^#I&Ej?0GDF=h<&hxb}iqGX4b| zmD`3}>IEsRy|O7tAyMi!!Y5BhfcU58$0fH+);Npf< zJeO}0JZFI|C~1wC1ejCB{)Fcvg{uU81ytGimY@-8k%lSHpgYdbNx>DP;R~Pv6Og%R zFt)69h!fW(ET@dKxyrIob*?L5->$7{EQwq8uqvNh~IpZyRER}=+Y6B7O zI}K3LpK_9SUzD$p8`|3J$KTiYX4@a@rp^gAy0NL$mNs7Wq zQ_r(>!;YE{L&zkpFO}}iVipT)#d}8G57iS_l=@ug0E|MfA4L;)g)E=l^}$CM?-WVX zJJItL+rZPGDLhHMUF`>MDWdcTbv5^WyG9cZAUgKv(OVsP?@d9cXiLTT6e-`O$%)G+ z&W;3q__pinZ6}n5*Fi%Wd!Ax*nf8zE2j95+a)cYE_hU-bnJlNCBzc!C)Y&~&LSIpZ%;r5xrk2W^lElV(ASZ_C+q?SXWb-u>MF5iEYeD;#MnDd zvQ5R)#1liHuK~wE{qvf54PBX1$LXr;n<(vgZJNm=Cx;0ir|Dl zkiNd5S#pmWhQM`doRj^7jo%<^_)x`R@sVerrPQ78j;4uEP4)*q+ptZiOgx=PZIW8t zv7<6d{cv3HGeg*}zqB98ZXfJi(Wt=9+8}@R*|4QnO%Gqiv`Br<$C(?vCE9aR10sYmmtPQD>&i~En>xdR-L-o=na zST@LjdHfFpr+X0Dj@q}He$&ZX&nx<}DiEIE)6Sjw35=4z?pIxxC&y7-Wnty#u2K8^ zKRnjk#RMSB`zaNBE`6$!+p|JeD#mIb0Z@m2-ja5?^2YZXqM(}6uxYEwpL-8(lpm=3 zeB$7D%8x%IpMGEe0*GfV=hlbJZ2Ei6#yZO0g(!~Nw)yMW&OB({=AR3%5HAv3c7Hlw z^oPW-w)4x>-&fae?XG_Sr$BLRyRTD!tbhLB8P@(oq75LhfJ~Nb8Vg+UAKP?mEEtLn zC$N!BwrUz1Rq+qj!q!^*g|+AsIA|tEKaGQ{;28FEjQ$gAS<6bIWm`GRnM<%1XU@OA zpDq4-AN-Q^z5n+&+MRMlXWX|*dP56OOioB(2w&bR(F=vTQnR}eq)UM>KqX1 zw{b@HwY1Q`t-h63G(2g+YhK2nL}nw$3MDl0lOFz4cC$92FJ}36c_(c;<7t<2$3L6& z31>gw+6q5jgt8P(W}gCN6}>bPoZTfMj&(P$QC>d1Gf0iGcpLXikoyoSbAR}^Aa`A} z%6#q+Ftkve)GvO`UR)rj?o3)uZ%&%uP){*LJ*JOm?|tM0KdhwVK#l?xO3Cg%4BGaK z946aiWgO6>NKwIR{jVF1&wwUO{JPPIFi#BLlFyXfXjHCv=J$=(BDv9Q{_RG~`;Qw9 znEJ09E%SeWqp{+ckSPrk<**sG1Q5T}$Y2otsPv=_l#voTw6v+(^5+Pj!aO`tDgqSx zP2%*S_6W(1hT91JFE`pA5Z0$`a`I()=$m0`!V#-QFqYJ3al>`}@AR9C9BVBGRh8zF zE_Tffnv+JBEfaFFh27LKoZ?#UShu_ zW=(D6yXf}QTHs%YnpN7bLy$d5-CZ+NM=Z*e9_)_w)*UIsT;8MGB7h{`C%Tt{rJ>YM z@{&pd+7*VnuYa457sN^rBI;vDUJi(3F}G9=)+XP}Tq5f)mzC>E(BPu9n%ZFzvG1i1 zn92?sC@dge$Ie1ZZtSbl(R}KKj(YVD*J`$nN^&P+3wnP%*671;s9)}3cq40qE{TV2 zXqaPZ_@p$=BR{B@LX&1$ps-m78l*YGs9;^!Dm(WJI6$DyM;aD{k!G^h-bv4Sib1tr z;&v7Mi1Hx?vqx^5&fu?qqCPbvml9U-<2T}cEL&`urgDMzHY_9}PH0k|5r5jmaqf#K zeIH&ow3yr^R?FLuco_=Z`(?_@-A}9ioj?WvONY+gzdv*F@hOrRY(d5^ft){N{SLg zooq(r|7L-mH)Cltd0SeDx$AZ^ytG(#2Nrt%AvKXVXz-G->4R_MWELak?rZ2MhuXu0tZS z(&Rm)UsxYupy1R)w^79vV z2x5f95uC&~!d_Yh>Nm6+KeII21zawd<+Cs+mV3T{CiFcC$Hn5Z9n}`^Dr6ot61lI3 z{fbt%o>^~MAf8C4`XtOj8#+f!cqCBkmEvEQkfz2I92}fb{C5|2s?LmW`gvGC1+9QJ z`NU1=&Bv#By~Hs`oRP7v+LK%IjH}O`x(FKzsq0}b6LD=GgaIAD5s8=9v1;{#rvp@1>Z=Iwg!P+&9QaUlR=o%1_j`7&kvK)-Uq3~w!@$HRUH+n?EfZc zw>r_FARi8?iW}cxRch{FnHSfzjC2vO3cf7}iQWyWg%4Yf6=*s`P%%?*Vu*aV<$8ou zv@AP!MWHMpZfGq}T=T_R8zky99)iL7F2oSi*~a~={S*{?xE*q@v-OtNqFGDdYXtE1 zv*?qdZCLeZhdT}oPOY~I-X-GHo932>a zwW+Xy|JdqW9~E@5d(+nHcS~i4)9zkRq-B7?Ds?N&8QWRu>pEtv*?*}%cEEJBBGjGVEhYLd*Kk{`RMsh< z_0CYiAg?Ipek(iXnm>=;ud2{>0*J6ry8T8KSbV(;SV}1!_;t|~Ur$&sR~80vg7v*q z&fHNGV+-);+&e|fefFh>1v|axy!1>)EJ3>JrGk@VHBr8pb1b3MoN;uphI0#~31q*& z!|k9tIA<~9%vhGr*yS^4mLs0-RhdQ0n_Gvt`-qnO98W?%a@?9K=1LSK2F<7{3H<}%Li0JU$zm#26TqKfRrq~UYE$L*hex%Z__4k)Gb z6$l!V`fo2sQj#BO$*&X`D8&Q)^CzeO`=3Zl4NDFerQgn!|5eqQMKyJ;0eGL283LY! zF%aYg0%7!q01_1;AwdEN1d0=&1OWxK5CJPHY6gas!ONr!S|&k*XpIPHL2W?5fTF0V z)M|~0F7?_L9D1p3xsA8J_`cV-*Z;EDxBmSP5Rop@G!G~ZHIfNBP0N*H#1RHQ>W_(K z?~I4Kv!7;bg?6%E?XZt>(+>BLu_L#|cMsU$#o z8x$s`HEbfbb~Hl=J`kB&Wvi&4CZCm>^gpc(wAz{Y`VT+p$bBuY)Fa`o#@Gs&s&}ux zv?rNPaVhP|U*`Q^MWfxq{!tF9J?!3oiIxAOIIOm4Wu0mIIDv--qD#w$%6I58kMp{h zQjxh0lUlpsFMXP_nY*L!tXZOFK(uYTnGsv7do@X6v`lqXF@0nPud*+hUtgFtqnCY^ zVmCR&Y~sdjaEcNQhFSx21kb8zMk@@Zl|i9KpiE3+HO0n|Ai$+C<3NR>`Z6(=F;VK( z^^E7(B#Ke+9Jy~u7LBf&WTyZl;n!wMi=u-ZF%~EZ#E9RFGKDaEv=&HVd8Np0bms7` zHycP6M`MGOyn?XSpvn;E?+gldXb33YuX$&qkQML%#;|UOK)`s2T{H8*Jpu+p)&k5z zc=>V}o@hGATSWJfU{}f3OkTxf<|lqc$Ut)+?WF46Fgd$*-;z3r&HS{ltzNz=ThAjSmNq z8lgOLkgu4`u(H>$WgZQt&$D$DjK?r@Ys@ojtwTC~@(nHMzo{m9u&QdTkL^L}3(HNVK#U8j9Q!%v|DHjo~{{K;V}ZKa6J=OP1g z&r{N1aSJeIgAAz>yIEUg1)5VVBm*{M+JMqf)Fd;_K75SVAfJ>8z3K=rGaj9F(jP;O@<8H>2_#!;>|3ct;Uyvrb9 znJqA0BIc0)x@m&%=i@S^kRQF7{4izoiYdFunAE(F#;KzLZPEP3SwpT>@R!FHWqp|% z#w}2IE57V1di{{h$nzoY#Re&&S8t|TiT-9R+Z!HZTKg;aqhle5wKkAvhW;B zt%ycrJzg+f73zi=58}9Y=`c}tn)PAd4cNgy=*)-+gwpitQhF{ims^^`E zp0RnLyyFZGuX}8(W4Dm{^th!WJ#<5+G`g5Xrg;PKe3$I^@AzP0aDkt(E*y_3dtm}Z z7#gSmB)UXm=_rVZjYlZoVuQ2R3-D{#)0F60bWQRk%SZu=fBCuP;I_@kc`kg}Zmtln zZ?$9XfL)AIKy3FkA8!Og@&GPTMP;2lViTzy4$nBtdphl7gd+;60YP`7PUwWJU(PdX zU+Wmhir1%9-mMY*bp>z-`%0#Qk%wKX1Dy2hkq=JB)77cTq04H&_$Bg?C1XMgEB#Hd zcyRy247wbh#wWH7Qa5SjstoslnVm>OcX{#3s(=O!0PiO!Fx9p)D?PxNSqst@i5?iY zF~Fc_oiu)h!*3nXHCHb{Gxc`xz32_j4~rM1+p>YuTI0-?Wab!GDNa^ux5f)yfejyL zn-O6hjRi)MJx(FQ-2M$|LrlD)r?IEjKQSk{T76-w8&(3GwQp&-DjY4QC5_WJ-TZ`# zFRW4=&-Lz}gssxU{1rg;w9FA5zYyyuta1GM(QEP#1Z1^oWgu~A_VO-l30)2>FPGX8 zCAN-Iy~9khU9h!Q&YLWD=)0`KKR?uOcIPf}B7A%BezIb>9W*j(jD4%|>@S^?J6!8c z)fDerSEhe;Uv;#x`|DHJ>rO#nNhisar@}yO1u%X+29MPgxw5q62NyZSEX@PVjWd!| zE(ZIUH}`vlMq!mNz%3+b@P1`2`%brS=Q~TPx!0C4qKH*xkzoin9||p>-C*H-i|9$N z0p(^}Y4V;(3q^j2~YcQ`-x)F#iY!-(9ZKH^hRJKd40;zJh*b58@LQSn_VvR-UF ziaH+ygkRs0XkZs@b~Rc^kKj=REtoUM;?nvv6>svI@tRjvV}B zt2vHCmCq)V9jL~vKhC(dMm03`T7^x>>P2DIp`{TBC`W8F-G=94_($HD@l&wNOmp&ad4iNy!Abrb0VL5o(QDv4iNZ%)yD$Gb!!{bHv#to~(YFu`V z!4!D#-v@hnGM=E2$37ac%x&&$yRw#ThyBMUDW8LH{vS**fH@#U8pQBWIw@S-_}uQ` zx+|X5%#~7?)x&C6b9?Tq!SsSF?gtCxxfO;xlNK=}NdNqOqMrlrLt_vg7#V=hppaBO zDQxDkm}EEerR}%X3OvcJMwsj%lv5ppj?Pqk(eCmoT*Bv&N6@uT;kp|j$Ed5C`3iiL zblNbP&uI~SF5k;#c1&0jyJWG4$SaIVG+)V$BQnsBLXG`s(2NCq-ma9LUrK=o1ZE7R Qk3vlVFAcWLLcxl^0VsbCeE + + + + Dashboard Page + + +

+Back to home +

The dashboard page

+Servo Project +
+ + \ No newline at end of file diff --git a/tests/html/service-worker/demo_iframe.html b/tests/html/service-worker/demo_iframe.html new file mode 100644 index 00000000000..f016b5d71aa --- /dev/null +++ b/tests/html/service-worker/demo_iframe.html @@ -0,0 +1,13 @@ + + + + + An iframe + + + + + + \ No newline at end of file diff --git a/tests/html/service-worker/dummy.js b/tests/html/service-worker/dummy.js new file mode 100644 index 00000000000..e627bfc5539 --- /dev/null +++ b/tests/html/service-worker/dummy.js @@ -0,0 +1 @@ +console.log("dummy test file"); \ No newline at end of file diff --git a/tests/html/service-worker/iframe_sw.js b/tests/html/service-worker/iframe_sw.js new file mode 100644 index 00000000000..c57a92d7bf3 --- /dev/null +++ b/tests/html/service-worker/iframe_sw.js @@ -0,0 +1 @@ +console.log("iframe service worker active"); \ No newline at end of file diff --git a/tests/html/service-worker/index.html b/tests/html/service-worker/index.html new file mode 100644 index 00000000000..ab11b754f31 --- /dev/null +++ b/tests/html/service-worker/index.html @@ -0,0 +1,43 @@ + + + + + Service Worker Tests + + +

This page tests the registration and initialization of Service Workers in Servo.

+
+ Go to Dashboard + Go to Profile + +
+ + + +
+ + + \ No newline at end of file diff --git a/tests/html/service-worker/profile.html b/tests/html/service-worker/profile.html new file mode 100644 index 00000000000..93348123564 --- /dev/null +++ b/tests/html/service-worker/profile.html @@ -0,0 +1,14 @@ + + + + + Profile Page + + +
+Back to home +

The profile page

+Servo Project +
+ + \ No newline at end of file diff --git a/tests/html/service-worker/sw.js b/tests/html/service-worker/sw.js new file mode 100644 index 00000000000..df748f34ebb --- /dev/null +++ b/tests/html/service-worker/sw.js @@ -0,0 +1 @@ +console.log("root service worker: active"); \ No newline at end of file diff --git a/tests/html/service-worker/sw_dashboard.js b/tests/html/service-worker/sw_dashboard.js new file mode 100644 index 00000000000..1d18a5d47dd --- /dev/null +++ b/tests/html/service-worker/sw_dashboard.js @@ -0,0 +1,2 @@ +importScripts('dummy.js'); +console.log("dashboard service worker: active"); diff --git a/tests/html/service-worker/sw_profile.js b/tests/html/service-worker/sw_profile.js new file mode 100644 index 00000000000..10e206ce01a --- /dev/null +++ b/tests/html/service-worker/sw_profile.js @@ -0,0 +1 @@ +console.log("profile service worker: active"); \ No newline at end of file diff --git a/tests/unit/net/data_loader.rs b/tests/unit/net/data_loader.rs index 959575be4c0..f73331a53be 100644 --- a/tests/unit/net/data_loader.rs +++ b/tests/unit/net/data_loader.rs @@ -8,7 +8,7 @@ use ipc_channel::ipc; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; use net_traits::LoadConsumer::Channel; use net_traits::ProgressMsg::{Payload, Done}; -use net_traits::{LoadData, LoadContext, NetworkError, LoadOrigin, RequestSource}; +use net_traits::{LoadData, LoadContext, NetworkError, LoadOrigin}; use self::hyper::header::ContentType; use self::hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; use url::Url; @@ -22,9 +22,6 @@ impl LoadOrigin for DataLoadTest { fn referrer_policy(&self) -> Option { None } - fn request_source(&self) -> RequestSource { - RequestSource::None - } fn pipeline_id(&self) -> Option { None } diff --git a/tests/unit/net/http_loader.rs b/tests/unit/net/http_loader.rs index aa3ff9c697d..dad9d3d945c 100644 --- a/tests/unit/net/http_loader.rs +++ b/tests/unit/net/http_loader.rs @@ -25,7 +25,7 @@ use net::hsts::HstsEntry; use net::http_loader::{LoadErrorType, HttpResponse}; use net::http_loader::{load, LoadError, HttpRequestFactory, HttpRequest, UIProvider, HttpState}; use net::resource_thread::{AuthCacheEntry, CancellationListener}; -use net_traits::{CustomResponse, RequestSource, Metadata, LoadOrigin}; +use net_traits::{CustomResponse, Metadata, LoadOrigin}; use net_traits::{LoadData, CookieSource, LoadContext, IncludeSubdomains}; use std::borrow::Cow; use std::io::{self, Write, Read, Cursor}; @@ -46,9 +46,6 @@ impl LoadOrigin for HttpTest { fn referrer_policy(&self) -> Option { None } - fn request_source(&self) -> RequestSource { - RequestSource::None - } fn pipeline_id(&self) -> Option { Some(PipelineId::fake_root_pipeline_id()) } @@ -66,9 +63,6 @@ impl<'a> LoadOrigin for LoadOriginInfo<'a> { fn referrer_policy(&self) -> Option { self.referrer_policy.clone() } - fn request_source(&self) -> RequestSource { - RequestSource::None - } fn pipeline_id(&self) -> Option { None } @@ -176,11 +170,6 @@ fn redirect_with_headers(host: String, mut headers: Headers) -> MockResponse { ) } -enum Source { - Window, - Worker -} - fn respond_404() -> MockResponse { MockResponse::new( Headers::new(), @@ -426,7 +415,7 @@ fn test_check_default_headers_loaded_in_every_request() { &AssertMustHaveHeadersRequestFactory { expected_headers: headers.clone(), body: <[_]>::to_vec(&[]) - }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); // Testing for method.POST load_data.method = Method::Post; @@ -437,7 +426,7 @@ fn test_check_default_headers_loaded_in_every_request() { &AssertMustHaveHeadersRequestFactory { expected_headers: headers, body: <[_]>::to_vec(&[]) - }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); } #[test] @@ -459,7 +448,7 @@ fn test_load_when_request_is_not_get_or_head_and_there_is_no_body_content_length None, &AssertMustIncludeHeadersRequestFactory { expected_headers: content_length, body: <[_]>::to_vec(&[]) - }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); } #[test] @@ -490,7 +479,7 @@ fn test_request_and_response_data_with_network_messages() { request_headers.set(Host { hostname: "bar.foo".to_owned(), port: None }); load_data.headers = request_headers.clone(); let _ = load(&load_data, &ui_provider, &http_state, Some(devtools_chan), &Factory, - DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); // notification received from devtools let devhttprequest = expect_devtools_http_request(&devtools_port); @@ -563,9 +552,6 @@ impl LoadOrigin for HttpTestNoPipeline { fn referrer_policy(&self) -> Option { None } - fn request_source(&self) -> RequestSource { - RequestSource::None - } fn pipeline_id(&self) -> Option { None } @@ -594,7 +580,7 @@ fn test_request_and_response_message_from_devtool_without_pipeline_id() { let (devtools_chan, devtools_port) = mpsc::channel::(); let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTestNoPipeline); let _ = load(&load_data, &ui_provider, &http_state, Some(devtools_chan), &Factory, - DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); // notification received from devtools assert!(devtools_port.try_recv().is_err()); @@ -629,7 +615,7 @@ fn test_load_when_redirecting_from_a_post_should_rewrite_next_request_as_get() { let ui_provider = TestProvider::new(); let _ = load(&load_data, &ui_provider, &http_state, None, &Factory, - DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); } #[test] @@ -660,7 +646,8 @@ fn test_load_should_decode_the_response_as_deflate_when_response_headers_have_co &load_data, &ui_provider, &http_state, None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)) + &CancellationListener::new(None), + None) .unwrap(); assert_eq!(read_response(&mut response), "Yay!"); @@ -695,7 +682,8 @@ fn test_load_should_decode_the_response_as_gzip_when_response_headers_have_conte &ui_provider, &http_state, None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)) + &CancellationListener::new(None), + None) .unwrap(); assert_eq!(read_response(&mut response), "Yay!"); @@ -740,7 +728,8 @@ fn test_load_doesnt_send_request_body_on_any_redirect() { None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), + None); } #[test] @@ -770,7 +759,8 @@ fn test_load_doesnt_add_host_to_sts_list_when_url_is_http_even_if_sts_headers_ar None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), + None); assert_eq!(http_state.hsts_list.read().unwrap().is_host_secure("mozilla.com"), false); } @@ -802,7 +792,8 @@ fn test_load_adds_host_to_sts_list_when_url_is_https_and_sts_headers_are_present None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), + None); assert!(http_state.hsts_list.read().unwrap().is_host_secure("mozilla.com")); } @@ -836,7 +827,8 @@ fn test_load_sets_cookies_in_the_resource_manager_when_it_get_set_cookie_header_ None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), + None); assert_cookie_for_domain(http_state.cookie_jar.clone(), "http://mozilla.com", "mozillaIs=theBest"); } @@ -870,7 +862,7 @@ fn test_load_sets_requests_cookies_header_for_url_by_getting_cookies_from_the_re expected_headers: cookie, body: <[_]>::to_vec(&*load_data.data.unwrap()) }, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), None); } #[test] @@ -912,7 +904,7 @@ fn test_load_sends_secure_cookie_if_http_changed_to_https_due_to_entry_in_hsts_s &AssertMustIncludeHeadersRequestFactory { expected_headers: headers, body: <[_]>::to_vec(&*load_data.data.unwrap()) - }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); } #[test] @@ -944,7 +936,7 @@ fn test_load_sends_cookie_if_nonhttp() { &AssertMustIncludeHeadersRequestFactory { expected_headers: headers, body: <[_]>::to_vec(&*load_data.data.unwrap()) - }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); } #[test] @@ -973,7 +965,7 @@ fn test_cookie_set_with_httponly_should_not_be_available_using_getcookiesforurl( None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), None); let mut cookie_jar = http_state.cookie_jar.write().unwrap(); assert!(cookie_jar.cookies_for_url(&url, CookieSource::NonHTTP).is_none()); @@ -1003,7 +995,7 @@ fn test_when_cookie_received_marked_secure_is_ignored_for_http() { None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), None); assert_cookie_for_domain(http_state.cookie_jar.clone(), "http://mozilla.com", ""); } @@ -1037,7 +1029,7 @@ fn test_when_cookie_set_marked_httpsonly_secure_isnt_sent_on_http_request() { &AssertMustNotIncludeHeadersRequestFactory { headers_not_expected: vec!["Cookie".to_owned()], body: <[_]>::to_vec(&*load_data.data.unwrap()) - }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); } #[test] @@ -1060,7 +1052,7 @@ fn test_load_sets_content_length_to_length_of_request_body() { expected_headers: content_len_headers, body: <[_]>::to_vec(&*load_data.data.unwrap()) }, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), None); } #[test] @@ -1086,7 +1078,7 @@ fn test_load_uses_explicit_accept_from_headers_in_load_data() { expected_headers: accept_headers, body: <[_]>::to_vec("Yay!".as_bytes()) }, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), None); } #[test] @@ -1114,7 +1106,7 @@ fn test_load_sets_default_accept_to_html_xhtml_xml_and_then_anything_else() { expected_headers: accept_headers, body: <[_]>::to_vec("Yay!".as_bytes()) }, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), None); } #[test] @@ -1137,7 +1129,7 @@ fn test_load_uses_explicit_accept_encoding_from_load_data_headers() { expected_headers: accept_encoding_headers, body: <[_]>::to_vec("Yay!".as_bytes()) }, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), None); } #[test] @@ -1161,7 +1153,7 @@ fn test_load_sets_default_accept_encoding_to_gzip_and_deflate() { expected_headers: accept_encoding_headers, body: <[_]>::to_vec("Yay!".as_bytes()) }, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), None); } #[test] @@ -1189,7 +1181,7 @@ fn test_load_errors_when_there_a_redirect_loop() { let ui_provider = TestProvider::new(); match load(&load_data, &ui_provider, &http_state, None, &Factory, - DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)) { + DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None) { Err(ref load_err) if load_err.error == LoadErrorType::RedirectLoop => (), _ => panic!("expected max redirects to fail") } @@ -1222,7 +1214,7 @@ fn test_load_errors_when_there_is_too_many_redirects() { prefs::PrefValue::Number(redirect_limit)); match load(&load_data, &ui_provider, &http_state, None, &Factory, - DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)) { + DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None) { Err(LoadError { error: LoadErrorType::MaxRedirects(num_redirects), url, .. }) => { assert_eq!(num_redirects, redirect_limit as u32); @@ -1264,7 +1256,7 @@ fn test_load_follows_a_redirect() { let ui_provider = TestProvider::new(); match load(&load_data, &ui_provider, &http_state, None, &Factory, - DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)) { + DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None) { Err(e) => panic!("expected to follow a redirect {:?}", e), Ok(mut lr) => { let response = read_response(&mut lr); @@ -1296,7 +1288,7 @@ fn test_load_errors_when_scheme_is_not_http_or_https() { None, &DontConnectFactory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)) { + &CancellationListener::new(None), None) { Err(ref load_err) if load_err.error == LoadErrorType::UnsupportedScheme { scheme: "ftp".into() } => (), _ => panic!("expected ftp scheme to be unsupported") } @@ -1315,7 +1307,7 @@ fn test_load_errors_when_viewing_source_and_inner_url_scheme_is_not_http_or_http None, &DontConnectFactory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)) { + &CancellationListener::new(None), None) { Err(ref load_err) if load_err.error == LoadErrorType::UnsupportedScheme { scheme: "ftp".into() } => (), _ => panic!("expected ftp scheme to be unsupported") } @@ -1357,7 +1349,7 @@ fn test_load_errors_when_cancelled() { None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &cancel_listener) { + &cancel_listener, None) { Err(ref load_err) if load_err.error == LoadErrorType::Cancelled => (), _ => panic!("expected load cancelled error!") } @@ -1427,7 +1419,7 @@ fn test_redirect_from_x_to_y_provides_y_cookies_from_y() { None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)) { + &CancellationListener::new(None), None) { Err(e) => panic!("expected to follow a redirect {:?}", e), Ok(mut lr) => { let response = read_response(&mut lr); @@ -1473,7 +1465,7 @@ fn test_redirect_from_x_to_x_provides_x_with_cookie_from_first_response() { None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)) { + &CancellationListener::new(None), None) { Err(e) => panic!("expected to follow a redirect {:?}", e), Ok(mut lr) => { let response = read_response(&mut lr); @@ -1515,7 +1507,7 @@ fn test_if_auth_creds_not_in_url_but_in_cache_it_sets_it() { None, &AssertMustIncludeHeadersRequestFactory { expected_headers: auth_header, body: <[_]>::to_vec(&[]) - }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); } #[test] @@ -1542,7 +1534,7 @@ fn test_auth_ui_sets_header_on_401() { None, &AssertAuthHeaderRequestFactory { expected_headers: auth_header, body: <[_]>::to_vec(&[]) - }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)) { + }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None) { Err(e) => panic!("response contained error {:?}", e), Ok(response) => { assert_eq!(response.metadata.status, Some(RawStatus(200, Cow::Borrowed("Ok")))); @@ -1575,7 +1567,7 @@ fn test_auth_ui_needs_www_auth() { let response = load(&load_data, &AuthProvider, &http_state, None, &Factory, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), None); match response { Err(e) => panic!("response contained error {:?}", e), Ok(response) => { @@ -1604,7 +1596,7 @@ fn assert_referer_header_matches(origin_info: &LoadOrigin, expected_headers: referer_headers, body: <[_]>::to_vec(&[]) }, DEFAULT_USER_AGENT.to_owned(), - &CancellationListener::new(None)); + &CancellationListener::new(None), None); } fn assert_referer_header_not_included(origin_info: &LoadOrigin, request_url: &str) { @@ -1622,7 +1614,7 @@ fn assert_referer_header_not_included(origin_info: &LoadOrigin, request_url: &st &AssertMustNotIncludeHeadersRequestFactory { headers_not_expected: vec!["Referer".to_owned()], body: <[_]>::to_vec(&[]) - }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); } #[test] @@ -1860,7 +1852,7 @@ fn test_no_referer_set_with_noreferrer_policy() { assert_referer_header_not_included(&origin_info, request_url) } -fn load_request_with_source(source: Source, expected_body: Vec) -> (Metadata, String) { +fn load_request_for_custom_response(expected_body: Vec) -> (Metadata, String) { use ipc_channel::ipc; let (sender, receiver) = ipc::channel().unwrap(); @@ -1880,16 +1872,11 @@ fn load_request_with_source(source: Source, expected_body: Vec) -> (Metadata let url = Url::parse("http://mozilla.com").unwrap(); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); - - match source { - Source::Window => load_data.source = RequestSource::Window(sender.clone()), - Source::Worker => load_data.source = RequestSource::Worker(sender.clone()), - } + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let join_handle = thread::spawn(move || { let response = load(&load_data.clone(), &ui_provider, &http_state, - None, &Factory, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + None, &Factory, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), Some(sender)); match response { Ok(mut response) => { let metadata = response.metadata.clone(); @@ -1900,24 +1887,16 @@ fn load_request_with_source(source: Source, expected_body: Vec) -> (Metadata } }); - let network_sender = receiver.recv().unwrap(); - network_sender.send(Some(mock_response)).unwrap(); + let mediator = receiver.recv().unwrap(); + mediator.response_chan.send(Some(mock_response)).unwrap(); let (metadata, body) = join_handle.join().unwrap(); (metadata, body) } #[test] -fn test_custom_response_from_window() { - let expected_body = b"Yay! From Window".to_vec(); - let (metadata, body) = load_request_with_source(Source::Window, expected_body.clone()); - assert_eq!(metadata.status, Some(RawStatus(200, Cow::Borrowed("OK")))); - assert_eq!(body, String::from_utf8(expected_body).unwrap()); -} - -#[test] -fn test_custom_response_from_worker() { - let expected_body = b"Yay! From Worker".to_vec(); - let (metadata, body) = load_request_with_source(Source::Worker, expected_body.clone()); +fn test_custom_response() { + let expected_body = b"Yay!".to_vec(); + let (metadata, body) = load_request_for_custom_response(expected_body.clone()); assert_eq!(metadata.status, Some(RawStatus(200, Cow::Borrowed("OK")))); assert_eq!(body, String::from_utf8(expected_body).unwrap()); } @@ -1963,7 +1942,7 @@ fn test_content_blocked() { None, &AssertMustNotIncludeHeadersRequestFactory { headers_not_expected: vec!["Cookie".to_owned()], body: b"hi".to_vec(), - }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); match response { Ok(_) => {}, _ => panic!("request should have succeeded without cookies"), @@ -1974,7 +1953,7 @@ fn test_content_blocked() { let response = load( &load_data, &ui_provider, &http_state, None, &Factory, - DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None); match response { Err(LoadError { error: LoadErrorType::ContentBlocked, .. }) => {}, _ => panic!("request should have been blocked"), diff --git a/tests/unit/net/resource_thread.rs b/tests/unit/net/resource_thread.rs index 5e298926e6f..1820ea73039 100644 --- a/tests/unit/net/resource_thread.rs +++ b/tests/unit/net/resource_thread.rs @@ -8,7 +8,7 @@ use net::filemanager_thread::{FileManagerThreadFactory, TFDProvider}; use net::resource_thread::new_core_resource_thread; use net_traits::hosts::{parse_hostsfile, host_replacement}; use net_traits::{CoreResourceMsg, LoadData, LoadConsumer, LoadContext}; -use net_traits::{NetworkError, ProgressMsg, LoadOrigin, RequestSource}; +use net_traits::{NetworkError, ProgressMsg, LoadOrigin}; use profile_traits::time::ProfilerChan; use std::borrow::ToOwned; use std::collections::HashMap; @@ -31,9 +31,6 @@ impl LoadOrigin for ResourceTest { fn referrer_policy(&self) -> Option { None } - fn request_source(&self) -> RequestSource { - RequestSource::None - } fn pipeline_id(&self) -> Option { None }